From 7f85e802c2bff1d9b0e8d7c05f32f10b60506648 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:54:10 +0900 Subject: [PATCH 01/26] fix(Reddit - Disable screenshot popup): Patch does not work in certain environments https://github.com/inotia00/ReVanced_Extended/issues/2891 --- .../layout/screenshotpopup/Fingerprints.kt | 45 +++++++++++++++++ .../screenshotpopup/ScreenshotPopupPatch.kt | 49 ++++++++++++++++++- .../utils/resourceid/SharedResourceIdPatch.kt | 25 ++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt index 01f630b2b..42d350793 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt @@ -1,5 +1,8 @@ package app.revanced.patches.reddit.layout.screenshotpopup +import app.revanced.patches.reddit.utils.resourceid.actionShare +import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner +import app.revanced.util.containsLiteralInstruction import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.or import com.android.tools.smali.dexlib2.AccessFlags @@ -13,3 +16,45 @@ internal val screenshotBannerContainerFingerprint = legacyFingerprint( "scope", ) ) + +/** + * Reddit 2025.06.0 ~ + */ +internal val screenshotTakenBannerComposableFingerprint = legacyFingerprint( + name = "screenshotTakenBannerComposableFingerprint", + returnType = "L", + customFingerprint = { method, classDef -> + method.containsLiteralInstruction(actionShare) && + method.containsLiteralInstruction(screenShotShareBanner) && + classDef.type.startsWith("Lcom/reddit/sharing/screenshot/composables/") && + method.name == "invoke" + } +) + +/** + * ~ Reddit 2025.05.1 + */ +internal val screenshotTakenBannerLambdaActionFingerprint = legacyFingerprint( + name = "screenshotTakenBannerLambdaFingerprint", + returnType = "V", + parameters = listOf("Landroidx/compose/runtime/", "I"), + customFingerprint = { method, _ -> + method.containsLiteralInstruction(actionShare) && + method.name == "invoke" + } +) + +/** + * ~ Reddit 2025.05.1 + */ +internal val screenshotTakenBannerLambdaBannerFingerprint = legacyFingerprint( + name = "screenshotTakenBannerLambdaFingerprint", + returnType = "V", + parameters = listOf("Landroidx/compose/runtime/", "I"), + customFingerprint = { method, _ -> + method.containsLiteralInstruction(screenShotShareBanner) && + method.name == "invoke" + } +) + + diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt index 4de050ce9..3b43169b2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt @@ -9,6 +9,10 @@ import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH import app.revanced.patches.reddit.utils.patch.PatchList.DISABLE_SCREENSHOT_POPUP +import app.revanced.patches.reddit.utils.resourceid.actionShare +import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner +import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch +import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater import app.revanced.patches.reddit.utils.settings.settingsPatch import app.revanced.patches.reddit.utils.settings.updatePatchStatus import app.revanced.util.findMutableMethodOf @@ -17,6 +21,8 @@ import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.indexOfFirstStringInstruction import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method @@ -33,7 +39,10 @@ val screenshotPopupPatch = bytecodePatch( ) { compatibleWith(COMPATIBLE_PACKAGE) - dependsOn(settingsPatch) + dependsOn( + settingsPatch, + sharedResourceIdPatch, + ) execute { @@ -107,6 +116,44 @@ val screenshotPopupPatch = bytecodePatch( throw PatchException("Failed to find hook method") } + if (is_2025_06_or_greater) { + screenshotTakenBannerComposableFingerprint.methodOrThrow().apply { + arrayOf( + actionShare, + screenShotShareBanner + ).forEach { literal -> + val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) + val insertIndex = indexOfFirstInstructionReversedOrThrow(literalIndex, Opcode.CONST_4) + val insertRegister = getInstruction(insertIndex).registerA + val jumpIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.SGET_OBJECT) + + addInstructionsWithLabels( + insertIndex, """ + invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR + move-result v$insertRegister + if-nez v$insertRegister, :hidden + """, ExternalLabel("hidden", getInstruction(jumpIndex)) + ) + } + } + } else { + arrayOf( + screenshotTakenBannerLambdaActionFingerprint, + screenshotTakenBannerLambdaBannerFingerprint + ).forEach { fingerprint -> + fingerprint.methodOrThrow().addInstructionsWithLabels( + 0, """ + invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR + move-result v0 + if-eqz v0, :ignore + return-void + :ignore + nop + """ + ) + } + } + updatePatchStatus( "enableScreenshotPopup", DISABLE_SCREENSHOT_POPUP diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt new file mode 100644 index 000000000..cb0ec3341 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.reddit.utils.resourceid + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.shared.mapping.ResourceType.STRING +import app.revanced.patches.shared.mapping.getResourceId +import app.revanced.patches.shared.mapping.resourceMappingPatch + +var actionShare = -1L + private set +var nsfwDialogTitle = -1L + private set +var screenShotShareBanner = -1L + private set + +internal val sharedResourceIdPatch = resourcePatch( + description = "sharedResourceIdPatch" +) { + dependsOn(resourceMappingPatch) + + execute { + actionShare = getResourceId(STRING, "action_share") + nsfwDialogTitle = getResourceId(STRING, "nsfw_dialog_title") + screenShotShareBanner = getResourceId(STRING, "screenshot_share_banner_title") + } +} \ No newline at end of file From b439ef3ee7523032707f6292c0788b5defa2e9c4 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:01:10 +0900 Subject: [PATCH 02/26] fix(Reddit - Remove subreddit dialog): New type of NSFW dialog not hidden https://github.com/inotia00/ReVanced_Extended/issues/2895 --- .../patches/RemoveSubRedditDialogPatch.java | 32 ++++++++- .../layout/subredditdialog/Fingerprints.kt | 38 +++++++++-- .../subredditdialog/SubRedditDialogPatch.kt | 67 ++++++++++++++++--- 3 files changed, 117 insertions(+), 20 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java index dc6672633..a92e406e4 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java @@ -2,7 +2,10 @@ package app.revanced.extension.reddit.patches; import static app.revanced.extension.shared.utils.StringRef.str; +import android.app.Dialog; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.widget.TextView; import androidx.annotation.NonNull; @@ -30,8 +33,33 @@ public class RemoveSubRedditDialogPatch { clickViewDelayed(cancelButtonView); } - public static boolean spoofHasBeenVisitedStatus(boolean hasBeenVisited) { - return Settings.REMOVE_NSFW_DIALOG.get() || hasBeenVisited; + public static void dismissNSFWDialog(Object customDialog) { + if (Settings.REMOVE_NSFW_DIALOG.get() && + customDialog instanceof Dialog dialog) { + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.height = 0; + params.width = 0; + + // Change the size of dialog to 0. + window.setAttributes(params); + + // Disable dialog's background dim. + window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + // Hide DecorView. + View decorView = window.getDecorView(); + decorView.setVisibility(View.GONE); + + // Dismiss dialog. + dialog.dismiss(); + } + } + } + + public static boolean removeNSFWDialog() { + return Settings.REMOVE_NSFW_DIALOG.get(); } public static boolean spoofLoggedInStatus(boolean isLoggedIn) { diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt index cae1dc6d8..06bd1b5b4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt @@ -1,5 +1,6 @@ package app.revanced.patches.reddit.layout.subredditdialog +import app.revanced.patches.reddit.utils.resourceid.nsfwDialogTitle import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction @@ -9,6 +10,7 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference internal val frequentUpdatesSheetScreenFingerprint = legacyFingerprint( name = "frequentUpdatesSheetScreenFingerprint", @@ -52,6 +54,13 @@ fun listOfIsLoggedInInstruction(method: Method) = ?.reversed() ?: emptyList() +fun indexOfGetOverInstruction(method: Method) = + method.indexOfFirstInstruction { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.name == "getOver18" + } + internal val nsfwAlertEmitFingerprint = legacyFingerprint( name = "nsfwAlertEmitFingerprint", returnType = "Ljava/lang/Object;", @@ -59,17 +68,32 @@ internal val nsfwAlertEmitFingerprint = legacyFingerprint( strings = listOf("reddit://reddit/r/", "nsfwAlertDelegate"), customFingerprint = { method, _ -> method.name == "emit" && - indexOfHasBeenVisitedInstruction(method) >= 0 + indexOfGetOverInstruction(method) >= 0 } ) -fun indexOfHasBeenVisitedInstruction(method: Method) = - method.indexOfFirstInstruction { - val reference = getReference() - opcode == Opcode.INVOKE_VIRTUAL && - reference?.name == "getHasBeenVisited" && - reference.returnType == "Z" +internal val nsfwAlertObserverFingerprint = legacyFingerprint( + name = "nsfwAlertObserverFingerprint", + returnType = "Ljava/lang/Object;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + strings = listOf("nsfwAlertDelegate"), + customFingerprint = { method, _ -> + method.name == "invokeSuspend" && + indexOfGetOverInstruction(method) >= 0 && + method.indexOfFirstInstruction { + opcode == Opcode.NEW_INSTANCE && + getReference()?.type?.startsWith("Lcom/reddit/frontpage/presentation/detail/DetailHolderPresenter\$showDialogIfNeverVisitedOrSubscribed\$") == true + } >= 0 } +) + +internal val nsfwAlertBuilderFingerprint = legacyFingerprint( + name = "nsfwAlertBuilderFingerprint", + literals = listOf(nsfwDialogTitle), + customFingerprint = { method, _ -> + method.definingClass.startsWith("Lcom/reddit/screen/nsfw") + } +) internal val redditAlertDialogsFingerprint = legacyFingerprint( name = "redditAlertDialogsFingerprint", diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt index ccd7e4064..db196001c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt @@ -2,19 +2,26 @@ package app.revanced.patches.reddit.layout.subredditdialog import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH import app.revanced.patches.reddit.utils.patch.PatchList.REMOVE_SUBREDDIT_DIALOG +import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.reddit.utils.settings.is_2024_41_or_greater import app.revanced.patches.reddit.utils.settings.is_2025_01_or_greater import app.revanced.patches.reddit.utils.settings.is_2025_05_or_greater import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater import app.revanced.patches.reddit.utils.settings.settingsPatch import app.revanced.patches.reddit.utils.settings.updatePatchStatus +import app.revanced.util.findFreeRegister import app.revanced.util.fingerprint.methodOrThrow +import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -32,7 +39,10 @@ val subRedditDialogPatch = bytecodePatch( ) { compatibleWith(COMPATIBLE_PACKAGE) - dependsOn(settingsPatch) + dependsOn( + settingsPatch, + sharedResourceIdPatch, + ) execute { @@ -70,17 +80,52 @@ val subRedditDialogPatch = bytecodePatch( } if (is_2025_01_or_greater) { - nsfwAlertEmitFingerprint.methodOrThrow().apply { - val hasBeenVisitedIndex = indexOfHasBeenVisitedInstruction(this) - val hasBeenVisitedRegister = - getInstruction(hasBeenVisitedIndex + 1).registerA + arrayOf( + nsfwAlertEmitFingerprint, + nsfwAlertObserverFingerprint + ).forEach { fingerprint -> + fingerprint.methodOrThrow().apply { + val getOverIndex = indexOfGetOverInstruction(this) + val getOverRegister = getInstruction(getOverIndex).registerC + val freeRegister = findFreeRegister(getOverIndex, getOverRegister) - addInstructions( - hasBeenVisitedIndex + 2, """ - invoke-static {v$hasBeenVisitedRegister}, $EXTENSION_CLASS_DESCRIPTOR->spoofHasBeenVisitedStatus(Z)Z - move-result v$hasBeenVisitedRegister - """ - ) + val returnIndex = indexOfFirstInstructionOrThrow(getOverIndex, Opcode.RETURN_OBJECT) + val jumpIndex = indexOfFirstInstructionReversedOrThrow(returnIndex, Opcode.SGET_OBJECT) + + addInstructionsWithLabels( + getOverIndex, """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->removeNSFWDialog()Z + move-result v$freeRegister + if-nez v$freeRegister, :jump + """, ExternalLabel("jump", getInstruction(jumpIndex)) + ) + } + } + + var hookCount = 0 + + nsfwAlertBuilderFingerprint.mutableClassOrThrow().let { + it.methods.forEach { method -> + method.apply { + val showIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.name == "show" + } + if (showIndex >= 0) { + val dialogRegister = getInstruction(showIndex + 1).registerA + + addInstruction( + showIndex + 2, + "invoke-static {v$dialogRegister}, $EXTENSION_CLASS_DESCRIPTOR->dismissNSFWDialog(Ljava/lang/Object;)V" + ) + hookCount++ + } + } + } + } + + if (hookCount == 0) { + throw PatchException("Failed to find hook method") } } From 09a8eb71142a642b328e87e4963a3e6ab19908ba Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:05:06 +0900 Subject: [PATCH 03/26] fix(YouTube - Custom branding icon): Splash animation background color is always white https://github.com/inotia00/ReVanced_Extended/issues/2892 --- .../branding/icon/CustomBrandingIconPatch.kt | 21 +++--- .../fix/splash/DarkModeSplashScreenPatch.kt | 73 +++++++++++++------ .../utils/playservice/VersionCheckPatch.kt | 5 +- 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt index 1ccc3e575..e954f02b8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt @@ -217,8 +217,16 @@ val customBrandingIconPatch = resourcePatch( } } - val styleList = if (is_19_32_or_greater) - listOf( + val styleList = mutableListOf( + Triple( + "values-v31", + "Base.Theme.YouTube.Launcher", + "@style/Theme.AppCompat.DayNight.NoActionBar" + ), + ) + + if (is_19_32_or_greater) { + styleList += listOf( Triple( "values-night-v31", "Theme.YouTube.Home", @@ -230,14 +238,7 @@ val customBrandingIconPatch = resourcePatch( "@style/Base.V27.Theme.YouTube.Home" ), ) - else - listOf( - Triple( - "values-v31", - "Base.Theme.YouTube.Launcher", - "@style/Theme.AppCompat.DayNight.NoActionBar" - ), - ) + } styleList.forEach { (directory, nodeAttributeName, nodeAttributeParent) -> document("res/$directory/styles.xml").use { document -> diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt index 2e3276993..f67961e91 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt @@ -1,8 +1,11 @@ package app.revanced.patches.youtube.utils.fix.splash import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch +import app.revanced.patches.youtube.utils.settings.ResourceUtils.youtubePackageName +import app.revanced.util.findElementByAttributeValueOrThrow import org.w3c.dom.Element /** @@ -25,31 +28,55 @@ val darkModeSplashScreenPatch = resourcePatch( } arrayOf( - "values-night" to "@style/Base.V23.Theme.YouTube.Home", - "values-night-v27" to "@style/Base.V27.Theme.YouTube.Home", - ).forEach { (directory, parent) -> + "values-night", + "values-night-v27", + ).forEach { directory -> document("res/$directory/styles.xml").use { document -> - // Create a night mode specific override for the splash screen background. - val style = document.createElement("style") - style.setAttribute("name", "Theme.YouTube.Home") - style.setAttribute("parent", parent) - - val colorSplashBackgroundColor = "@color/yt_black1" - arrayOf( - "android:navigationBarColor" to colorSplashBackgroundColor, - "android:windowBackground" to colorSplashBackgroundColor, - "android:colorBackground" to colorSplashBackgroundColor, - "colorPrimaryDark" to colorSplashBackgroundColor, - "android:windowLightStatusBar" to "false", - ).forEach { (name, value) -> - val styleItem = document.createElement("item") - styleItem.setAttribute("name", name) - styleItem.textContent = value - style.appendChild(styleItem) - } - val resourcesNode = document.getElementsByTagName("resources").item(0) as Element - resourcesNode.appendChild(style) + val childNodes = resourcesNode.childNodes + + for (i in 0 until childNodes.length) { + val node = childNodes.item(i) as? Element ?: continue + val nodeAttributeName = node.getAttribute("name") + if (nodeAttributeName.startsWith("Theme.YouTube.Launcher")) { + val nodeAttributeParent = node.getAttribute("parent") + + val style = document.createElement("style") + style.setAttribute("name", "Theme.YouTube.Home") + style.setAttribute("parent", nodeAttributeParent) + + val colorSplashBackgroundColor = "@color/yt_black1" + arrayOf( + "android:navigationBarColor" to colorSplashBackgroundColor, + "android:windowBackground" to colorSplashBackgroundColor, + "android:colorBackground" to colorSplashBackgroundColor, + "colorPrimaryDark" to colorSplashBackgroundColor, + "android:windowLightStatusBar" to "false", + ).forEach { (name, value) -> + val styleItem = document.createElement("item") + styleItem.setAttribute("name", name) + styleItem.textContent = value + style.appendChild(styleItem) + } + + resourcesNode.removeChild(node) + resourcesNode.appendChild(style) + } + } + } + } + } + + finalize { + // GmsCore support included + if (youtubePackageName != YOUTUBE_PACKAGE_NAME) { + document("AndroidManifest.xml").use { document -> + val mainActivityElement = document.childNodes.findElementByAttributeValueOrThrow( + "android:name", + "com.google.android.apps.youtube.app.watchwhile.MainActivity", + ) + + mainActivityElement.setAttribute("android:launchMode", "singleTask") } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt index 19bf4774f..11e9b7239 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt @@ -55,6 +55,8 @@ var is_19_36_or_greater = false private set var is_19_41_or_greater = false private set +var is_19_42_or_greater = false + private set var is_19_43_or_greater = false private set var is_19_44_or_greater = false @@ -116,7 +118,8 @@ val versionCheckPatch = resourcePatch( is_19_32_or_greater = 243305000 <= playStoreServicesVersion is_19_34_or_greater = 243499000 <= playStoreServicesVersion is_19_36_or_greater = 243705000 <= playStoreServicesVersion - is_19_41_or_greater = 244305000 <= playStoreServicesVersion + is_19_41_or_greater = 244205000 <= playStoreServicesVersion + is_19_42_or_greater = 244305000 <= playStoreServicesVersion is_19_43_or_greater = 244405000 <= playStoreServicesVersion is_19_44_or_greater = 244505000 <= playStoreServicesVersion is_19_46_or_greater = 244705000 <= playStoreServicesVersion From bccd6dc5df7deebd6765283f3fc270437e3dbcf3 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:09:11 +0900 Subject: [PATCH 04/26] fix(YouTube - Swipe controls): Patch fails on YouTube 19.43.41 when `Player components` patch is included --- .../youtube/swipe/controls/Fingerprints.kt | 21 ++++++++++++------- .../swipe/controls/SwipeControlsPatch.kt | 16 +++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/Fingerprints.kt index 838a52ecf..eecf2a6cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/Fingerprints.kt @@ -10,6 +10,7 @@ import app.revanced.util.indexOfFirstInstruction import app.revanced.util.or 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 fullScreenEngagementOverlayFingerprint = legacyFingerprint( @@ -113,11 +114,17 @@ internal val playerGestureConfigSyntheticFingerprint = legacyFingerprint( // This method is always called "a" because this kind of class always has a single method. method.name == "a" && classDef.methods.count() == 2 && - method.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" && - reference.parameterTypes.isEmpty() && - reference.returnType == "Z" - } >= 0 + indexOfPlayerConfigModelBooleanInstruction(method) >= 0 }, -) \ No newline at end of file +) + +internal fun indexOfPlayerConfigModelBooleanInstruction( + method: Method, + startIndex: Int = 0 +) = method.indexOfFirstInstruction(startIndex) { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" && + reference.parameterTypes.isEmpty() && + reference.returnType == "Z" +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/SwipeControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/SwipeControlsPatch.kt index 3b82fb404..ea22e133b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/SwipeControlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/swipe/controls/SwipeControlsPatch.kt @@ -30,7 +30,6 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.util.ResourceGroup import app.revanced.util.copyResources import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall -import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference @@ -212,14 +211,17 @@ val swipeControlsPatch = bytecodePatch( // region patch for disable swipe to enter fullscreen mode (in the player) and disable swipe to exit fullscreen mode - playerGestureConfigSyntheticFingerprint.matchOrThrow().let { - val endIndex = it.patternMatch!!.endIndex + playerGestureConfigSyntheticFingerprint.methodOrThrow().apply { + val disableSwipeToExitFullscreenModeIndex = + indexOfPlayerConfigModelBooleanInstruction(this) + val disableSwipeToEnterFullscreenModeInThePlayerIndex = + indexOfPlayerConfigModelBooleanInstruction(this, disableSwipeToExitFullscreenModeIndex + 1) mapOf( - 3 to "disableSwipeToEnterFullscreenModeInThePlayer", - 9 to "disableSwipeToExitFullscreenMode" - ).forEach { (offSet, methodName) -> - it.getWalkerMethod(endIndex - offSet).apply { + disableSwipeToExitFullscreenModeIndex to "disableSwipeToExitFullscreenMode", + disableSwipeToEnterFullscreenModeInThePlayerIndex to "disableSwipeToEnterFullscreenModeInThePlayer" + ).forEach { (walkerIndex, methodName) -> + getWalkerMethod(walkerIndex).apply { val index = implementation!!.instructions.lastIndex val register = getInstruction(index).registerA From f761bc45ec9e6275809579fd465c32a86417b486 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:12:59 +0900 Subject: [PATCH 05/26] feat(YouTube - Hook download actions, Overlay buttons): Add `Add to queue and reload video` and `Remove from queue and reload video` settings, fixed a bug where the queue was unavailable under certain conditions --- .../youtube/patches/utils/PlaylistPatch.java | 92 +++++++++++++++---- .../utils/requests/EditPlaylistRequest.kt | 3 +- .../extension/youtube/utils/VideoUtils.java | 23 ++++- .../utils/dismiss/DismissPlayerHookPatch.kt | 83 +++++++++++++++++ .../youtube/utils/dismiss/Fingerprints.kt | 17 ++++ .../youtube/utils/playlist/PlaylistPatch.kt | 36 +++++--- .../youtube/settings/host/values/strings.xml | 2 + 7 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/Fingerprints.kt diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java index 484ad319a..0e157be43 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java @@ -30,6 +30,8 @@ import app.revanced.extension.youtube.patches.utils.requests.EditPlaylistRequest import app.revanced.extension.youtube.patches.utils.requests.GetPlaylistsRequest; import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest; import app.revanced.extension.youtube.settings.Settings; +import app.revanced.extension.youtube.shared.PlayerType; +import app.revanced.extension.youtube.shared.VideoInformation; import app.revanced.extension.youtube.utils.ExtendedUtils; import app.revanced.extension.youtube.utils.VideoUtils; import kotlin.Pair; @@ -124,6 +126,7 @@ public class PlaylistPatch extends VideoUtils { if (videoId != null) { lastVideoIds.remove(videoId, setVideoId); EditPlaylistRequest.clearVideoId(videoId); + Logger.printDebug(() -> "Video removed by YouTube flyout menu: " + videoId); } } } @@ -182,9 +185,22 @@ public class PlaylistPatch extends VideoUtils { } else { videoId = currentVideoId; synchronized (lastVideoIds) { - QueueManager[] customActionsEntries = playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null - ? QueueManager.addToQueueEntries - : QueueManager.removeFromQueueEntries; + QueueManager[] customActionsEntries; + boolean canReload = PlayerType.getCurrent().isMaximizedOrFullscreen() && + lastVideoIds.get(VideoInformation.getVideoId()) != null; + if (playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null) { + if (canReload) { + customActionsEntries = QueueManager.addToQueueWithReloadEntries; + } else { + customActionsEntries = QueueManager.addToQueueEntries; + } + } else { + if (canReload) { + customActionsEntries = QueueManager.removeFromQueueWithReloadEntries; + } else { + customActionsEntries = QueueManager.removeFromQueueEntries; + } + } buildBottomSheetDialog(customActionsEntries); } @@ -213,7 +229,8 @@ public class PlaylistPatch extends VideoUtils { ExtendedUtils.showBottomSheetDialog(mContext, mScrollView, actionsMap); } - private static void fetchQueue(boolean remove, boolean openPlaylist, boolean openVideo) { + private static void fetchQueue(boolean remove, boolean openPlaylist, + boolean openVideo, boolean reload) { try { String currentPlaylistId = playlistId; String currentVideoId = videoId; @@ -233,7 +250,7 @@ public class PlaylistPatch extends VideoUtils { showToast(fetchSucceededCreate); Logger.printDebug(() -> "Queue successfully created, playlistId: " + createdPlaylistId + ", setVideoId: " + setVideoId); if (openPlaylist) { - openQueue(currentVideoId, openVideo); + openQueue(currentVideoId, openVideo, reload); } return; } @@ -251,22 +268,24 @@ public class PlaylistPatch extends VideoUtils { String fetchedSetVideoId = request.getResult(); Logger.printDebug(() -> "fetchedSetVideoId: " + fetchedSetVideoId); if (remove) { // Remove from queue. - if (StringUtils.isEmpty(fetchedSetVideoId)) { + if ("".equals(fetchedSetVideoId)) { lastVideoIds.remove(currentVideoId, setVideoId); + EditPlaylistRequest.clearVideoId(currentVideoId); showToast(fetchSucceededRemove); if (openPlaylist) { - openQueue(currentVideoId, openVideo); + openQueue(currentVideoId, openVideo, reload); } return; } showToast(fetchFailedRemove); } else { // Add to queue. - if (StringUtils.isNotEmpty(fetchedSetVideoId)) { + if (fetchedSetVideoId != null && !fetchedSetVideoId.isEmpty()) { lastVideoIds.putIfAbsent(currentVideoId, fetchedSetVideoId); + EditPlaylistRequest.clearVideoId(currentVideoId); showToast(fetchSucceededAdd); Logger.printDebug(() -> "Video successfully added, setVideoId: " + fetchedSetVideoId); if (openPlaylist) { - openQueue(currentVideoId, openVideo); + openQueue(currentVideoId, openVideo, reload); } return; } @@ -388,10 +407,10 @@ public class PlaylistPatch extends VideoUtils { } private static void openQueue() { - openQueue("", false); + openQueue("", false, false); } - private static void openQueue(String currentVideoId, boolean openVideo) { + private static void openQueue(String currentVideoId, boolean openVideo, boolean reload) { String currentPlaylistId = playlistId; if (currentPlaylistId.isEmpty()) { handleCheckError(checkFailedQueue); @@ -403,7 +422,15 @@ public class PlaylistPatch extends VideoUtils { return; } // Open a video from a playlist - openPlaylist(currentPlaylistId, currentVideoId); + if (reload) { + // Since the Queue is not automatically synced, a 'reload' action has been added as a workaround. + // The 'reload' action simply closes the video and reopens it. + // It is important to close the video, otherwise the Queue will not be updated. + dismissPlayer(); + openPlaylist(currentPlaylistId, VideoInformation.getVideoId(), true); + } else { + openPlaylist(currentPlaylistId, currentVideoId); + } } else { // Open a playlist openPlaylist(currentPlaylistId); @@ -422,27 +449,37 @@ public class PlaylistPatch extends VideoUtils { ADD_TO_QUEUE( "revanced_queue_manager_add_to_queue", "yt_outline_list_add_black_24", - () -> fetchQueue(false, false, false) + () -> fetchQueue(false, false, false, false) ), ADD_TO_QUEUE_AND_OPEN_QUEUE( "revanced_queue_manager_add_to_queue_and_open_queue", "yt_outline_list_add_black_24", - () -> fetchQueue(false, true, false) + () -> fetchQueue(false, true, false, false) ), ADD_TO_QUEUE_AND_PLAY_VIDEO( "revanced_queue_manager_add_to_queue_and_play_video", "yt_outline_list_play_arrow_black_24", - () -> fetchQueue(false, true, true) + () -> fetchQueue(false, true, true, false) + ), + ADD_TO_QUEUE_AND_RELOAD_VIDEO( + "revanced_queue_manager_add_to_queue_and_reload_video", + "yt_outline_arrow_circle_black_24", + () -> fetchQueue(false, true, true, true) ), REMOVE_FROM_QUEUE( "revanced_queue_manager_remove_from_queue", "yt_outline_trash_can_black_24", - () -> fetchQueue(true, false, false) + () -> fetchQueue(true, false, false, false) ), REMOVE_FROM_QUEUE_AND_OPEN_QUEUE( "revanced_queue_manager_remove_from_queue_and_open_queue", "yt_outline_trash_can_black_24", - () -> fetchQueue(true, true, false) + () -> fetchQueue(true, true, false, false) + ), + REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO( + "revanced_queue_manager_remove_from_queue_and_reload_video", + "yt_outline_arrow_circle_black_24", + () -> fetchQueue(true, true, true, true) ), OPEN_QUEUE( "revanced_queue_manager_open_queue", @@ -490,6 +527,17 @@ public class PlaylistPatch extends VideoUtils { SAVE_QUEUE, }; + public static final QueueManager[] addToQueueWithReloadEntries = { + ADD_TO_QUEUE, + ADD_TO_QUEUE_AND_OPEN_QUEUE, + ADD_TO_QUEUE_AND_PLAY_VIDEO, + ADD_TO_QUEUE_AND_RELOAD_VIDEO, + OPEN_QUEUE, + //REMOVE_QUEUE, + EXTERNAL_DOWNLOADER, + SAVE_QUEUE, + }; + public static final QueueManager[] removeFromQueueEntries = { REMOVE_FROM_QUEUE, REMOVE_FROM_QUEUE_AND_OPEN_QUEUE, @@ -499,6 +547,16 @@ public class PlaylistPatch extends VideoUtils { SAVE_QUEUE, }; + public static final QueueManager[] removeFromQueueWithReloadEntries = { + REMOVE_FROM_QUEUE, + REMOVE_FROM_QUEUE_AND_OPEN_QUEUE, + REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO, + OPEN_QUEUE, + //REMOVE_QUEUE, + EXTERNAL_DOWNLOADER, + SAVE_QUEUE, + }; + public static final QueueManager[] noVideoIdQueueEntries = { OPEN_QUEUE, //REMOVE_QUEUE, diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt index a76277543..aa369b8a5 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt @@ -8,7 +8,6 @@ import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.EDIT_PLA import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import org.apache.commons.lang3.StringUtils import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -216,7 +215,7 @@ class EditPlaylistRequest private constructor( dataSyncId, ) if (json != null) { - return parseResponse(json, StringUtils.isNotEmpty(setVideoId)) + return parseResponse(json, setVideoId != null && setVideoId.isNotEmpty()) } return null diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/VideoUtils.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/VideoUtils.java index 6d8d407c4..1671e734c 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/VideoUtils.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/VideoUtils.java @@ -133,13 +133,25 @@ public class VideoUtils extends IntentUtils { } public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId) { + openPlaylist(playlistId, videoId, false); + } + + public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId, boolean withTimestamp) { final StringBuilder sb = new StringBuilder(); if (videoId.isEmpty()) { sb.append(getPlaylistUrl(playlistId)); } else { - sb.append(getVideoScheme(videoId, false)); - sb.append("&list="); + sb.append(VIDEO_URL); + sb.append(videoId); + sb.append("?list="); sb.append(playlistId); + if (withTimestamp) { + final long currentVideoTimeInSeconds = VideoInformation.getVideoTimeInSeconds(); + if (currentVideoTimeInSeconds > 0) { + sb.append("&t="); + sb.append(currentVideoTimeInSeconds); + } + } } launchView(sb.toString(), getContext().getPackageName()); } @@ -269,6 +281,13 @@ public class VideoUtils extends IntentUtils { return !isExternalDownloaderLaunched.get() && original; } + /** + * Rest of the implementation added by patch. + */ + public static void dismissPlayer() { + Logger.printDebug(() -> "Dismiss player"); + } + /** * Rest of the implementation added by patch. */ diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt new file mode 100644 index 000000000..fb390deff --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt @@ -0,0 +1,83 @@ +package app.revanced.patches.youtube.utils.dismiss + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH +import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch +import app.revanced.util.addStaticFieldToExtension +import app.revanced.util.findMethodOrThrow +import app.revanced.util.fingerprint.methodOrThrow +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.indexOfFirstLiteralInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +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 + +private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR = + "$EXTENSION_PATH/utils/VideoUtils;" + +val dismissPlayerHookPatch = bytecodePatch( + description = "dismissPlayerHookPatch" +) { + dependsOn(sharedExtensionPatch) + + execute { + dismissPlayerOnClickListenerFingerprint.methodOrThrow().apply { + val literalIndex = + indexOfFirstLiteralInstructionOrThrow(DISMISS_PLAYER_LITERAL) + val dismissPlayerIndex = indexOfFirstInstructionOrThrow(literalIndex) { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.returnType == "V" && + reference.parameterTypes.isEmpty() + } + val dismissPlayerReference = + getInstruction(dismissPlayerIndex).reference as MethodReference + val dismissPlayerClass = dismissPlayerReference.definingClass + + val fieldIndex = + indexOfFirstInstructionReversedOrThrow(dismissPlayerIndex) { + opcode == Opcode.IGET_OBJECT && + getReference()?.type == dismissPlayerClass + } + val fieldReference = + getInstruction(fieldIndex).reference as FieldReference + + findMethodOrThrow(fieldReference.definingClass).apply { + val insertIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.IPUT_OBJECT && + getReference() == fieldReference + } + val insertRegister = + getInstruction(insertIndex).registerA + + addInstruction( + insertIndex, + "sput-object v$insertRegister, $EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR->dismissPlayerClass:$dismissPlayerClass" + ) + + val smaliInstructions = + """ + if-eqz v0, :ignore + invoke-virtual {v0}, $dismissPlayerReference + :ignore + return-void + """ + + addStaticFieldToExtension( + EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR, + "dismissPlayer", + "dismissPlayerClass", + dismissPlayerClass, + smaliInstructions, + false + ) + } + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/Fingerprints.kt new file mode 100644 index 000000000..98de1131b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.utils.dismiss + +import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.or +import com.android.tools.smali.dexlib2.AccessFlags + +internal const val DISMISS_PLAYER_LITERAL = 34699L + +internal val dismissPlayerOnClickListenerFingerprint = legacyFingerprint( + name = "dismissPlayerOnClickListenerFingerprint", + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + literals = listOf(DISMISS_PLAYER_LITERAL), + customFingerprint = { method, _ -> + method.name == "onClick" + } +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt index c0327377b..9f23ad2db 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt @@ -7,11 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.mainactivity.getMainActivityMethod +import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch +import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch import app.revanced.patches.youtube.utils.request.buildRequestPatch import app.revanced.patches.youtube.utils.request.hookBuildRequest +import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -28,6 +31,9 @@ val playlistPatch = bytecodePatch( dependsOn( sharedExtensionPatch, mainActivityResolvePatch, + dismissPlayerHookPatch, + playerTypeHookPatch, + videoInformationPatch, buildRequestPatch, ) @@ -56,12 +62,26 @@ val playlistPatch = bytecodePatch( """ ) + setPivotBarVisibilityFingerprint + .matchOrThrow(setPivotBarVisibilityParentFingerprint) + .let { + it.method.apply { + val viewIndex = it.patternMatch!!.startIndex + val viewRegister = getInstruction(viewIndex).registerA + addInstruction( + viewIndex + 1, + "invoke-static {v$viewRegister}," + + " $EXTENSION_CLASS_DESCRIPTOR->setPivotBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", + ) + } + } + + // Users deleted videos via YouTube's flyout menu. val setVideoIdReference = with(playlistEndpointFingerprint.methodOrThrow()) { val setVideoIdIndex = indexOfSetVideoIdInstruction(this) getInstruction(setVideoIdIndex).reference as FieldReference } - // Users deleted videos via YouTube's flyout menu. editPlaylistFingerprint .matchOrThrow(editPlaylistConstructorFingerprint) .let { @@ -86,19 +106,5 @@ val playlistPatch = bytecodePatch( ) } } - - setPivotBarVisibilityFingerprint - .matchOrThrow(setPivotBarVisibilityParentFingerprint) - .let { - it.method.apply { - val viewIndex = it.patternMatch!!.startIndex - val viewRegister = getInstruction(viewIndex).registerA - addInstruction( - viewIndex + 1, - "invoke-static {v$viewRegister}," + - " $EXTENSION_CLASS_DESCRIPTOR->setPivotBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", - ) - } - } } } diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml index cadaf9aef..2e355c1e1 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -26,11 +26,13 @@ Please download %2$s from the website." Add to queue Add to queue and open queue Add to queue and play video + Add to queue and reload video External downloader Open queue Queue Remove from queue Remove from queue and open queue + Remove from queue and reload video Remove queue Save queue "Instead of opening an external downloader, open the queue manager dialog. From 56b713b0dbe33053b162c825032826fbb9d056a2 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:13:36 +0900 Subject: [PATCH 06/26] fix build error --- .../kotlin/app/revanced/util/BytecodeUtils.kt | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index 1e5910f03..3b0b85818 100644 --- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -22,12 +22,16 @@ import app.revanced.patches.shared.mapping.resourceMappingPatch import app.revanced.util.Utils.printWarn import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.Opcode.* import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.MethodParameter import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i import com.android.tools.smali.dexlib2.iface.reference.MethodReference @@ -37,6 +41,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.util.MethodUtil +import java.util.EnumSet const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX" @@ -52,6 +57,121 @@ fun parametersEqual( return true } +/** + * Starting from and including the instruction at index [startIndex], + * finds the next register that is wrote to and not read from. If a return instruction + * is encountered, then the lowest unused register is returned. + * + * This method can return a non 4-bit register, and the calling code may need to temporarily + * swap register contents if a 4-bit register is required. + * + * @param startIndex Inclusive starting index. + * @param registersToExclude Registers to exclude, and consider as used. For most use cases, + * all registers used in injected code should be specified. + * @throws IllegalArgumentException If a branch or conditional statement is encountered + * before a suitable register is found. + */ +internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): Int { + if (implementation == null) { + throw IllegalArgumentException("Method has no implementation: $this") + } + if (startIndex < 0 || startIndex >= instructions.count()) { + throw IllegalArgumentException("startIndex out of bounds: $startIndex") + } + + // All registers used by an instruction. + fun Instruction.getRegistersUsed() = when (this) { + is FiveRegisterInstruction -> listOf(registerC, registerD, registerE, registerF, registerG) + is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC) + is TwoRegisterInstruction -> listOf(registerA, registerB) + is OneRegisterInstruction -> listOf(registerA) + is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList() + else -> emptyList() + } + + // Register that is written to by an instruction. + fun Instruction.getRegisterWritten() = when (this) { + is ThreeRegisterInstruction -> registerA + is TwoRegisterInstruction -> registerA + is OneRegisterInstruction -> registerA + else -> throw IllegalStateException("Not a write instruction: $this") + } + + val writeOpcodes = EnumSet.of( + NEW_INSTANCE, NEW_ARRAY, + MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT, + MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION, + IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT, + SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT, + ) + + val branchOpcodes = EnumSet.of( + GOTO, GOTO_16, GOTO_32, + IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE, + IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ, + ) + + val returnOpcodes = EnumSet.of( + RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, + ) + + // Highest 4-bit register available, exclusive. Ideally return a free register less than this. + val maxRegister4Bits = 16 + var bestFreeRegisterFound: Int? = null + val usedRegisters = registersToExclude.toMutableSet() + + for (i in startIndex until instructions.count()) { + val instruction = getInstruction(i) + + if (instruction.opcode in returnOpcodes) { + // Method returns. Use lowest register that hasn't been encountered. + val freeRegister = (0 until implementation!!.registerCount).find { + it !in usedRegisters + } + if (freeRegister != null) { + return freeRegister + } + if (bestFreeRegisterFound != null) { + return bestFreeRegisterFound; + } + + // Somehow every method register was read from before any register was wrote to. + // In practice this never occurs. + throw IllegalArgumentException("Could not find a free register from startIndex: " + + "$startIndex excluding: $registersToExclude") + } + + if (instruction.opcode in branchOpcodes) { + if (bestFreeRegisterFound != null) { + return bestFreeRegisterFound; + } + // This method is simple and does not follow branching. + throw IllegalArgumentException("Encountered a branch statement before a free register could be found") + } + + if (instruction.opcode in writeOpcodes) { + val freeRegister = instruction.getRegisterWritten() + if (freeRegister !in usedRegisters) { + if (freeRegister < maxRegister4Bits) { + // Found an ideal register. + return freeRegister + } + + // Continue searching for a 4-bit register if available. + if (bestFreeRegisterFound == null || freeRegister < bestFreeRegisterFound) { + bestFreeRegisterFound = freeRegister + } + } + } + + usedRegisters.addAll(instruction.getRegistersUsed()) + } + + // Cannot be reached since a branch or return statement will + // be encountered before the end of the method. + throw IllegalStateException() +} + /** * Find the [MutableMethod] from a given [Method] in a [MutableClass]. * From 5162dccecc5e2cb9bd51a9da65de89292b982efc Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:16:29 +0900 Subject: [PATCH 07/26] feat(YouTube - Settings): Add `App name`, `App version` and `Patched date` to patch information --- .../youtube/patches/utils/PatchStatus.java | 22 ++++++++++-------- .../ReVancedSettingsPreference.java | 23 +++++++++++++++++++ .../branding/name/CustomBrandingNamePatch.kt | 3 --- .../youtube/utils/settings/ResourceUtils.kt | 3 --- .../youtube/utils/settings/SettingsPatch.kt | 12 ++++++++++ .../youtube/settings/host/values/strings.xml | 8 ++++--- .../youtube/settings/xml/revanced_prefs.xml | 7 +++--- 7 files changed, 57 insertions(+), 21 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PatchStatus.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PatchStatus.java index d5317e396..cf8efd5db 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PatchStatus.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PatchStatus.java @@ -9,6 +9,11 @@ public class PatchStatus { return false; } + // Modified by a patch. Do not touch. + public static boolean OldSeekbarThumbnailsDefaultBoolean() { + return false; + } + public static boolean OldSplashAnimation() { // Replace this with true if the Restore old splash animation (Custom branding icon) succeeds return false; @@ -40,23 +45,22 @@ public class PatchStatus { return false; } - public static String SpoofAppVersionDefaultString() { - return "18.17.43"; - } - public static boolean ToolBarComponents() { // Replace this with true if the Toolbar components patch succeeds return false; } + public static long PatchedTime() { + return 0L; + } + + public static String SpoofAppVersionDefaultString() { + return "18.17.43"; + } + // Modified by a patch. Do not touch. public static String RVXMusicPackageName() { return "com.google.android.apps.youtube.music"; } - // Modified by a patch. Do not touch. - public static boolean OldSeekbarThumbnailsDefaultBoolean() { - return false; - } - } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedSettingsPreference.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedSettingsPreference.java index e4cc256bf..3cb96c7ae 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedSettingsPreference.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedSettingsPreference.java @@ -6,6 +6,8 @@ import static app.revanced.extension.shared.utils.Utils.isSDKAbove; import android.preference.Preference; import android.preference.SwitchPreference; +import java.util.Date; + import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch; import app.revanced.extension.youtube.patches.utils.PatchStatus; @@ -44,6 +46,7 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment { AmbientModePreferenceLinks(); FullScreenPanelPreferenceLinks(); NavigationPreferenceLinks(); + PatchInformationPreferenceLinks(); RYDPreferenceLinks(); SeekBarPreferenceLinks(); ShortsPreferenceLinks(); @@ -143,6 +146,26 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment { ); } + /** + * Set patch information preference summary + */ + private static void PatchInformationPreferenceLinks() { + Preference appNamePreference = mPreferenceManager.findPreference("revanced_app_name"); + if (appNamePreference != null) { + appNamePreference.setSummary(ExtendedUtils.getAppLabel()); + } + Preference appVersionPreference = mPreferenceManager.findPreference("revanced_app_version"); + if (appVersionPreference != null) { + appVersionPreference.setSummary(ExtendedUtils.getAppVersionName()); + } + Preference patchedDatePreference = mPreferenceManager.findPreference("revanced_patched_date"); + if (patchedDatePreference != null) { + long patchedTime = PatchStatus.PatchedTime(); + Date date = new Date(patchedTime); + patchedDatePreference.setSummary(date.toLocaleString()); + } + } + /** * Enable/Disable Preference related to RYD settings */ diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/name/CustomBrandingNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/name/CustomBrandingNamePatch.kt index a1fe0d202..1dca220ca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/name/CustomBrandingNamePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/name/CustomBrandingNamePatch.kt @@ -4,7 +4,6 @@ import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.stringOption import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_NAME_FOR_YOUTUBE -import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusLabel import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.util.removeStringsElements import app.revanced.util.valueOrThrow @@ -53,7 +52,5 @@ val customBrandingNamePatch = resourcePatch( .appendChild(stringElement) } - updatePatchStatusLabel(appName) - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/ResourceUtils.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/ResourceUtils.kt index 43cd0c4f1..094ca72fd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/ResourceUtils.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/ResourceUtils.kt @@ -88,9 +88,6 @@ internal object ResourceUtils { updatePatchStatusSettings("Icon", "@string/revanced_icon_$iconName") } - fun updatePatchStatusLabel(appName: String) = - updatePatchStatusSettings("Label", appName) - fun updatePatchStatusTheme(themeName: String) = updatePatchStatusSettings("Theme", themeName) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt index d3fe3da10..471e4cd71 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt @@ -1,15 +1,18 @@ package app.revanced.patches.youtube.utils.settings import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.stringOption +import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableLongEncodedValue import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_CLASS_DESCRIPTOR import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_PATH import app.revanced.patches.shared.mainactivity.injectConstructorMethodCall import app.revanced.patches.shared.mainactivity.injectOnCreateMethodCall import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE +import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.fix.attributes.themeAttributesPatch @@ -25,11 +28,13 @@ import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.copyResources import app.revanced.util.copyXmlNode import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.findMethodOrThrow import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.removeStringsElements import app.revanced.util.valueOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.immutable.value.ImmutableLongEncodedValue import org.w3c.dom.Element import java.nio.file.Files import java.util.jar.Manifest @@ -81,6 +86,13 @@ private val settingsBytecodePatch = bytecodePatch( EXTENSION_UTILS_CLASS_DESCRIPTOR, "setActivity" ) + + findMethodOrThrow(PATCH_STATUS_CLASS_DESCRIPTOR) { + name == "PatchedTime" + }.replaceInstruction( + 0, + "const-wide v0, ${MutableLongEncodedValue(ImmutableLongEncodedValue(System.currentTimeMillis()))}L" + ) } } diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml index 2e355c1e1..daa701e47 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -2303,8 +2303,11 @@ Click to see more information." Patch information Information about applied patches. - - Tool used + + App info + App name + App version + Patched date Others @@ -2316,7 +2319,6 @@ Click to see more information." Revancify Blue Revancify Red YouTube - Stock Excluded Included Stock diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml index 3b906b092..18ab51e47 100644 --- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -912,10 +912,12 @@ - + + + + - @@ -999,7 +1001,6 @@ - From 692b4f2c53e825cc567cd4644a0c71bea6c99238 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:19:33 +0900 Subject: [PATCH 08/26] feat(Translations): Update translation --- .../resources/youtube/translations/ar/strings.xml | 14 ++++++-------- .../youtube/translations/bg-rBG/strings.xml | 4 +--- .../youtube/translations/de-rDE/strings.xml | 4 +--- .../youtube/translations/el-rGR/strings.xml | 4 +--- .../youtube/translations/es-rES/strings.xml | 4 +--- .../youtube/translations/fr-rFR/strings.xml | 4 +--- .../youtube/translations/hu-rHU/strings.xml | 4 +--- .../youtube/translations/id-rID/strings.xml | 2 +- .../resources/youtube/translations/in/strings.xml | 2 +- .../youtube/translations/it-rIT/strings.xml | 4 +--- .../youtube/translations/ja-rJP/strings.xml | 10 ++++------ .../youtube/translations/ko-rKR/strings.xml | 10 +++++++--- .../youtube/translations/pl-rPL/strings.xml | 10 +++++++--- .../youtube/translations/pt-rBR/strings.xml | 4 +--- .../youtube/translations/ru-rRU/strings.xml | 4 +--- .../youtube/translations/tr-rTR/strings.xml | 4 +--- .../youtube/translations/uk-rUA/strings.xml | 4 +--- .../youtube/translations/vi-rVN/strings.xml | 4 +--- .../youtube/translations/zh-rCN/strings.xml | 4 +--- .../youtube/translations/zh-rTW/strings.xml | 4 +--- 20 files changed, 40 insertions(+), 64 deletions(-) diff --git a/patches/src/main/resources/youtube/translations/ar/strings.xml b/patches/src/main/resources/youtube/translations/ar/strings.xml index fead0585a..3b31f2cb2 100644 --- a/patches/src/main/resources/youtube/translations/ar/strings.xml +++ b/patches/src/main/resources/youtube/translations/ar/strings.xml @@ -360,8 +360,8 @@ تم تمكين فلتر القائمة المنبثقة بالموجز. تم تعطيل فلتر القائمة المنبثقة بالموجز. نوع تصفية القائمة المنبثقة للموجز - قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك استخدام <b>\'تشغيل التالي\'</b> أو <b>\'في قائمة المحتوى الرئيسي\'</b> ككلمات رئيسية. - قم بالتصفية إذا كانت هناك تطابقات.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك فقط استخدام <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b> ككلمات رئيسية. + قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك استخدام <b>\'تشغيل التالي\'</b> أو <b>\'في قائمة المحتوى الرئيسي\'</b> ككلمات مفتاحية. + قم بالتصفية إذا كانت هناك تطابقات.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك فقط استخدام <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b> ككلمات مفتاحية. تصفية القائمة المنبثقة بالموجز قائمة بأسماء القائمة المنبثقة المراد تصفيتها، مفصولة بسطور جديدة. @@ -562,8 +562,8 @@ "إخفاء عناصر قائمة الحساب وعلامة التبويب أنت. قد لا يتم إخفاء بعض المكونات." نوع تصفية قائمة الحساب - قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك استخدام <b>\'YouTube Premium\'</b> أو <b>\'Premium\'</b> ككلمات رئيسية. - قم بالتصفية إذا تطابقت النتائج.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك فقط استخدام <b>\'الحصول على YouTube Premium\'</b> ككلمات رئيسية. + قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك استخدام <b>\'YouTube Premium\'</b> أو <b>\'Premium\'</b> ككلمات مفتاحية. + قم بالتصفية إذا تطابقت النتائج.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك فقط استخدام <b>\'الحصول على YouTube Premium\'</b> ككلمات مفتاحية. تعديل فلتر قائمة الحساب قائمة بأسماء قائمة الحسابات المراد تصفيتها، مفصولة بسطور جديدة. إخفاء الاسم المعرِّف @@ -1140,7 +1140,7 @@ نوع المشغل المصغر معطَّل الأصلي - الحد الأدنى + Minimal الجهاز اللوحي حديث 1 حديث 2 @@ -2154,8 +2154,7 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص معلومات التعديل معلومات عن التعديلات المطبقة. - - الأداة المستخدمة + أخرى مخصص @@ -2166,7 +2165,6 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص Revancify Blue Revancify Red YouTube - الإفتراضي مستبعد مضمن الإفتراضي diff --git a/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml b/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml index 7674cc0c9..62d426282 100644 --- a/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml +++ b/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml @@ -1794,8 +1794,7 @@ AVC има максимална разделителна способност о Информация за корекции Информация за приложените корекции. - - Ползвани инструменти + Други По избор @@ -1806,7 +1805,6 @@ AVC има максимална разделителна способност о Revancify синя Revancify Червена YouTube - По подразбиране Изключване Включване По подразбиране diff --git a/patches/src/main/resources/youtube/translations/de-rDE/strings.xml b/patches/src/main/resources/youtube/translations/de-rDE/strings.xml index 530305072..47ebb30cf 100644 --- a/patches/src/main/resources/youtube/translations/de-rDE/strings.xml +++ b/patches/src/main/resources/youtube/translations/de-rDE/strings.xml @@ -2042,8 +2042,7 @@ Klicken Sie hier, um weitere Informationen zu sehen." Patch-Informationen Informationen über angewandte Patches - - Werkzeug verwendet + Andere Benutzerdefiniert @@ -2054,7 +2053,6 @@ Klicken Sie hier, um weitere Informationen zu sehen." Revancify Blau Revancify Rot YouTube - Stock ausgeschlossen Enthalten Stock diff --git a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml index 335baf89e..d4e4616e2 100644 --- a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml +++ b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml @@ -2162,8 +2162,7 @@ Playlists Πληροφορίες τροποποίησης Πληροφορίες σχετικά με τις εφαρμοσμένες τροποποιήσεις. - - Χρησιμοποιούμενο εργαλείο + Άλλα Προσαρμοσμένο @@ -2174,7 +2173,6 @@ Playlists Revancify Blue Revancify Red YouTube - Προεπιλογή Εξαιρέθηκε Συμπεριλήφθηκε Προεπιλογή diff --git a/patches/src/main/resources/youtube/translations/es-rES/strings.xml b/patches/src/main/resources/youtube/translations/es-rES/strings.xml index 6ccc07135..d6b4d4b79 100644 --- a/patches/src/main/resources/youtube/translations/es-rES/strings.xml +++ b/patches/src/main/resources/youtube/translations/es-rES/strings.xml @@ -2133,8 +2133,7 @@ Pulsa aquí para ver más información." Información de parches Información sobre los parches aplicados. - - Herramientas utilizadas + Otros Personalizado @@ -2145,7 +2144,6 @@ Pulsa aquí para ver más información." Revancify Blue Revancify Red YouTube - Predeterminada Excluidos Incluidos Predeterminado diff --git a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml index 810163c3d..8c00f96b1 100644 --- a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml +++ b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml @@ -2148,8 +2148,7 @@ Cliquez pour plus d'informations." Informations sur les patchs Informations sur les patchs appliqués. - - Outil utilisé + Autres Personnalisé @@ -2160,7 +2159,6 @@ Cliquez pour plus d'informations." Revancify Bleu Revancify Rouge Youtube - Officiel Exclus Appliqué Officiel diff --git a/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml b/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml index 61f16bae5..596ca233a 100644 --- a/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml +++ b/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml @@ -1914,8 +1914,7 @@ Kattintson a további információkért." Patch információ Információ az alkalmazott patchekről. - - Használt eszköz + Egyebek Egyéni @@ -1926,7 +1925,6 @@ Kattintson a további információkért." Revancify Kék Revancify Piros YouTube - Alap Kizárva Beleértve Alap diff --git a/patches/src/main/resources/youtube/translations/id-rID/strings.xml b/patches/src/main/resources/youtube/translations/id-rID/strings.xml index 8c9ecd0d9..6766196f5 100644 --- a/patches/src/main/resources/youtube/translations/id-rID/strings.xml +++ b/patches/src/main/resources/youtube/translations/id-rID/strings.xml @@ -400,6 +400,6 @@ Keterbatasan: Tombol Kembali pada bilah alat mungkin tidak berfungsi." - + diff --git a/patches/src/main/resources/youtube/translations/in/strings.xml b/patches/src/main/resources/youtube/translations/in/strings.xml index 8c9ecd0d9..6766196f5 100644 --- a/patches/src/main/resources/youtube/translations/in/strings.xml +++ b/patches/src/main/resources/youtube/translations/in/strings.xml @@ -400,6 +400,6 @@ Keterbatasan: Tombol Kembali pada bilah alat mungkin tidak berfungsi." - + diff --git a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml index 3c5133350..3dfa6da46 100644 --- a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml +++ b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml @@ -2142,8 +2142,7 @@ Tocca per vedere maggiori informazioni." Informazioni patch Informazioni sulle patch applicate. - - Strumenti usati + Altri Personalizzata @@ -2154,7 +2153,6 @@ Tocca per vedere maggiori informazioni." Revancify Blue Revancify Red YouTube - Predefinita Escluso Incluso Predefinito diff --git a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml index 8bd5b2d92..5bc6cc711 100644 --- a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml +++ b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml @@ -51,7 +51,7 @@ キューを削除しました。 動画を削除しました。 キューは「%s」に保存されました。 - YouTube と Revanced Extended 設定の言語 + Revanced Extended 設定の言語 アプリの設定言語に従う "" "" @@ -1664,8 +1664,8 @@ DeArrow の詳細については、ここをタップしてください。"トーストを表示 デフォルトの画質を変更した際にトーストが表示されるようにします。 デフォルトの画質を変更した際にトーストが表示されるようにします。 - モバイルデータ通信使用時のデフォルト画質 - Wi-Fi 使用時のデフォルト画質 + モバイルデータ通信使用時のショートのデフォルト画質 + Wi-Fi 使用時のショートのデフォルト画質 ショートの画質変更を記憶 現在の設定: 画質の変更はすべてのショートに適用されます。 現在の設定: 画質の変更は現在のショートにのみ適用されます。 @@ -2095,8 +2095,7 @@ iOS クライアント選択する場合は、これらの値が必要になる パッチ情報 適用されたパッチに関する情報です。 - - 使用されたツール + その他 カスタム @@ -2107,7 +2106,6 @@ iOS クライアント選択する場合は、これらの値が必要になる Revancify Blue Revancify Red YouTube - オリジナル 除外されています 適用されています オリジナル diff --git a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml index 782ea8c39..dda719bbe 100644 --- a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml +++ b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml @@ -22,11 +22,13 @@ 현재 재생목록에 추가 현재 재생목록에 추가하고 현재 재생목록 열기 현재 재생목록에 추가하고 동영상 재생 + 현재 재생목록에 추가하고 동영상 다시 로드 외부 다운로더 현재 재생목록 열기 현재 재생목록 현재 재생목록에서 제거 현재 재생목록에서 제거하고 현재 재생목록 열기 + 현재 재생목록에서 제거하고 동영상 다시 로드 현재 재생목록 제거 현재 재생목록 저장 "외부 다운로더가 아닌 현재 재생목록 관리자 다이얼로그를 실행할 수 있습니다. @@ -2158,8 +2160,11 @@ PoToken과 VisitorData를 발급받는 방법을 보려면 여기를 누르세 패치 정보 적용된 패치에 대한 정보입니다. - - 사용된 도구 + + 앱 정보 + 앱 이름 + 앱 버전 + 패치된 날짜 기타 사용자 정의 @@ -2170,7 +2175,6 @@ PoToken과 VisitorData를 발급받는 방법을 보려면 여기를 누르세 Revancify Blue Revancify Red YouTube - YouTube 제외된 패치 포함된 패치 YouTube diff --git a/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml b/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml index 43310e109..a4871199c 100644 --- a/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml +++ b/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml @@ -22,11 +22,13 @@ Pobierz %2$s ze strony internetowej." Dodaj do kolejki Dodaj do kolejki i otwórz kolejkę Dodaj do kolejki i odtwórz film + Dodaj do kolejki i odśwież film Zewnętrzna aplikacja od pobierania Otwórz kolejkę Kolejka Usuń z kolejki Usuń z kolejki i otwórz kolejkę + Usuń z kolejki i odśwież film Usuń kolejkę Zapisz kolejkę "Zamiast otwierać zewnętrzną aplikację od pobierania, otwiera okno menedżera kolejki. @@ -2153,8 +2155,11 @@ Kliknij, by zobaczyć więcej informacji." Informacje o łatkach Informacje na temat zastosowanych łatek. - - Użyte narzędzie + + Informacje o aplikacji + Nazwa aplikacji + Wersja aplikacji + Data załatania Inne Własna @@ -2165,7 +2170,6 @@ Kliknij, by zobaczyć więcej informacji." Niebieska od Revancify Czerwona od Revancify YouTube - Domyślne Wykluczone Zawarte Domyślny diff --git a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml index 08b66fcf1..4935df26b 100644 --- a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml +++ b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml @@ -1957,8 +1957,7 @@ Clique para ver mais informações." Informações do patch Informações sobre as modificações aplicadas. - - Ferramenta usada + Outros Personalizado @@ -1969,7 +1968,6 @@ Clique para ver mais informações." Revancify Blue Revancify Red YouTube - Padrão Excluído Incluído Padrão diff --git a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml index e87a1c83a..9fb4f7fc8 100644 --- a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml +++ b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml @@ -2164,8 +2164,7 @@ Shorts Информация о патчах Информация о примененных патчах. - - Используемые инструменты + Другие Пользовательский @@ -2176,7 +2175,6 @@ Shorts Revancify синяя Revancify красная Иконка YouTube - По умолчанию Не применён Применён По умолчанию diff --git a/patches/src/main/resources/youtube/translations/tr-rTR/strings.xml b/patches/src/main/resources/youtube/translations/tr-rTR/strings.xml index 2ca246e47..17312e1df 100644 --- a/patches/src/main/resources/youtube/translations/tr-rTR/strings.xml +++ b/patches/src/main/resources/youtube/translations/tr-rTR/strings.xml @@ -1545,8 +1545,7 @@ Devam düğmesine dokunun ve pil optimizasyonlarını devre dışı bırakın."< Yama Bilgileri Uygulanmış Yamalar Hakkında Bilgi. - - Araç kullanıldı + Diğerleri Özel @@ -1557,7 +1556,6 @@ Devam düğmesine dokunun ve pil optimizasyonlarını devre dışı bırakın."< Revancify Mavi Revancify Kırmızı YouTube - Stok Hariç tutulan Dahil Stok diff --git a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml index 4d5174517..28669b5eb 100644 --- a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml +++ b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml @@ -2147,8 +2147,7 @@ AVC має максимальну роздільну здатність 1080p, Інформація про патчі Інформація про застосовані патчі. - - Використано інструменти + Інше Користувацька @@ -2159,7 +2158,6 @@ AVC має максимальну роздільну здатність 1080p, Revancify синя Revancify червона YouTube - Стандартна Виключено Включено Стандартна diff --git a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml index d272eece7..c47d1c600 100644 --- a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml +++ b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml @@ -2150,8 +2150,7 @@ Nhấp vào đây để biết thêm chi tiết." Thông tin bản vá Thông tin về các bản vá được áp dụng. - - Công cụ được sử dụng + Khác Tùy chỉnh @@ -2162,7 +2161,6 @@ Nhấp vào đây để biết thêm chi tiết." Revancify Blue Revancify Red YouTube - Nguyên bản Đã loại trừ Đã bao gồm Nguyên bản diff --git a/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml b/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml index a77aecfd8..c6684b1d5 100644 --- a/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml +++ b/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml @@ -1629,8 +1629,7 @@ 补丁信息 已应用补丁的信息 - - 使用的工具 + 其他 Custom @@ -1641,7 +1640,6 @@ Revancify Blue Revancify Red YouTube - 默认 Excluded Included Stock diff --git a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml index 290bfe2cc..6b8941b29 100644 --- a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml +++ b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml @@ -2138,8 +2138,7 @@ AVC 的最大解析度為 1080p,Opus 音訊編解碼器不可用,影片播 補丁訊息 已應用補丁的訊息 - - 使用的工具 --唐懂翻譯 + 其他 自訂 @@ -2150,7 +2149,6 @@ AVC 的最大解析度為 1080p,Opus 音訊編解碼器不可用,影片播 復興藍 復興紅 YouTube - 預設 排除 包括 預設 From 1eca8c854c3eac8ae62f92e1e40a5134ad320f9e Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:21:14 +0900 Subject: [PATCH 09/26] bump 5.6.2-dev.1 --- gradle.properties | 2 +- patches.json | 6 ++++-- patches/api/patches.api | 13 ++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index fbb95566f..0b13e5b33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official kotlin.jvm.target.validation.mode = IGNORE -version = 5.6.1 +version = 5.6.2-dev.1 diff --git a/patches.json b/patches.json index cff3a24c6..4de24746a 100644 --- a/patches.json +++ b/patches.json @@ -1024,7 +1024,8 @@ "description": "Adds an option to disable the popup that appears when taking a screenshot.", "use": true, "dependencies": [ - "Settings for Reddit" + "Settings for Reddit", + "ResourcePatch" ], "compatiblePackages": { "com.reddit.frontpage": [ @@ -2211,7 +2212,8 @@ "description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.", "use": true, "dependencies": [ - "Settings for Reddit" + "Settings for Reddit", + "ResourcePatch" ], "compatiblePackages": { "com.reddit.frontpage": [ diff --git a/patches/api/patches.api b/patches/api/patches.api index 11f542538..f4d107275 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -414,7 +414,7 @@ public final class app/revanced/patches/reddit/layout/screenshotpopup/Screenshot } public final class app/revanced/patches/reddit/layout/subredditdialog/FingerprintsKt { - public static final fun indexOfHasBeenVisitedInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I + public static final fun indexOfGetOverInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I public static final fun indexOfSetBackgroundTintListInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I public static final fun listOfIsLoggedInInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/util/List; } @@ -458,6 +458,12 @@ public final class app/revanced/patches/reddit/utils/extension/SharedExtensionPa public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatchKt { + public static final fun getActionShare ()J + public static final fun getNsfwDialogTitle ()J + public static final fun getScreenShotShareBanner ()J +} + public final class app/revanced/patches/reddit/utils/settings/SettingsPatchKt { public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; public static final fun is_2024_26_or_greater ()Z @@ -903,6 +909,10 @@ public final class app/revanced/patches/youtube/utils/controlsoverlay/ControlsOv public static final fun getControlsOverlayConfigPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatchKt { + public static final fun getDismissPlayerHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatchKt { public static final fun getEngagementPanelHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1050,6 +1060,7 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa public static final fun is_19_34_or_greater ()Z public static final fun is_19_36_or_greater ()Z public static final fun is_19_41_or_greater ()Z + public static final fun is_19_42_or_greater ()Z public static final fun is_19_43_or_greater ()Z public static final fun is_19_44_or_greater ()Z public static final fun is_19_46_or_greater ()Z From 3f9edca15d5e3a09af504c9c78bc63538268a3a3 Mon Sep 17 00:00:00 2001 From: KobeW50 <84587632+KobeW50@users.noreply.github.com> Date: Mon, 31 Mar 2025 19:50:25 -0400 Subject: [PATCH 10/26] feat(YouTube - Settings): Place RVX settings at top of settings by default (#147) Co-authored-by: Kobe --- patches.json | 2 +- .../revanced/patches/youtube/utils/settings/SettingsPatch.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/patches.json b/patches.json index 4de24746a..72c3927ef 100644 --- a/patches.json +++ b/patches.json @@ -2510,7 +2510,7 @@ "description": "The settings menu name that the RVX settings menu should be above.", "required": true, "type": "kotlin.String", - "default": "@string/about_key", + "default": "@string/parent_tools_key", "values": { "Parent settings": "@string/parent_tools_key", "General": "@string/general_key", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt index 471e4cd71..60cd26b83 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt @@ -96,7 +96,7 @@ private val settingsBytecodePatch = bytecodePatch( } } -private const val DEFAULT_ELEMENT = "@string/about_key" +private const val DEFAULT_ELEMENT = "@string/parent_tools_key" private const val DEFAULT_LABEL = "RVX" private val SETTINGS_ELEMENTS_MAP = mapOf( From 15db05c6365be6417afa4c113bac2ffa09815972 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:49:47 +0900 Subject: [PATCH 11/26] fix(Reddit - Remove subreddit dialog): Can't open post from search results --- .../patches/RemoveSubRedditDialogPatch.java | 4 +++ .../layout/subredditdialog/Fingerprints.kt | 29 ++++------------- .../subredditdialog/SubRedditDialogPatch.kt | 32 ++++++------------- 3 files changed, 21 insertions(+), 44 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java index a92e406e4..fae8d0627 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/RemoveSubRedditDialogPatch.java @@ -33,6 +33,10 @@ public class RemoveSubRedditDialogPatch { clickViewDelayed(cancelButtonView); } + public static boolean spoofHasBeenVisitedStatus(boolean hasBeenVisited) { + return Settings.REMOVE_NSFW_DIALOG.get() || hasBeenVisited; + } + public static void dismissNSFWDialog(Object customDialog) { if (Settings.REMOVE_NSFW_DIALOG.get() && customDialog instanceof Dialog dialog) { diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt index 06bd1b5b4..bea8e6697 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/Fingerprints.kt @@ -10,7 +10,6 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference internal val frequentUpdatesSheetScreenFingerprint = legacyFingerprint( name = "frequentUpdatesSheetScreenFingerprint", @@ -54,13 +53,6 @@ fun listOfIsLoggedInInstruction(method: Method) = ?.reversed() ?: emptyList() -fun indexOfGetOverInstruction(method: Method) = - method.indexOfFirstInstruction { - val reference = getReference() - opcode == Opcode.INVOKE_VIRTUAL && - reference?.name == "getOver18" - } - internal val nsfwAlertEmitFingerprint = legacyFingerprint( name = "nsfwAlertEmitFingerprint", returnType = "Ljava/lang/Object;", @@ -68,24 +60,17 @@ internal val nsfwAlertEmitFingerprint = legacyFingerprint( strings = listOf("reddit://reddit/r/", "nsfwAlertDelegate"), customFingerprint = { method, _ -> method.name == "emit" && - indexOfGetOverInstruction(method) >= 0 + indexOfHasBeenVisitedInstruction(method) >= 0 } ) -internal val nsfwAlertObserverFingerprint = legacyFingerprint( - name = "nsfwAlertObserverFingerprint", - returnType = "Ljava/lang/Object;", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - strings = listOf("nsfwAlertDelegate"), - customFingerprint = { method, _ -> - method.name == "invokeSuspend" && - indexOfGetOverInstruction(method) >= 0 && - method.indexOfFirstInstruction { - opcode == Opcode.NEW_INSTANCE && - getReference()?.type?.startsWith("Lcom/reddit/frontpage/presentation/detail/DetailHolderPresenter\$showDialogIfNeverVisitedOrSubscribed\$") == true - } >= 0 +fun indexOfHasBeenVisitedInstruction(method: Method) = + method.indexOfFirstInstruction { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.name == "getHasBeenVisited" && + reference.returnType == "Z" } -) internal val nsfwAlertBuilderFingerprint = legacyFingerprint( name = "nsfwAlertBuilderFingerprint", diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt index db196001c..58e8d5fbc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/subredditdialog/SubRedditDialogPatch.kt @@ -2,11 +2,9 @@ package app.revanced.patches.reddit.layout.subredditdialog import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH import app.revanced.patches.reddit.utils.patch.PatchList.REMOVE_SUBREDDIT_DIALOG @@ -17,7 +15,6 @@ import app.revanced.patches.reddit.utils.settings.is_2025_05_or_greater import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater import app.revanced.patches.reddit.utils.settings.settingsPatch import app.revanced.patches.reddit.utils.settings.updatePatchStatus -import app.revanced.util.findFreeRegister import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference @@ -80,26 +77,17 @@ val subRedditDialogPatch = bytecodePatch( } if (is_2025_01_or_greater) { - arrayOf( - nsfwAlertEmitFingerprint, - nsfwAlertObserverFingerprint - ).forEach { fingerprint -> - fingerprint.methodOrThrow().apply { - val getOverIndex = indexOfGetOverInstruction(this) - val getOverRegister = getInstruction(getOverIndex).registerC - val freeRegister = findFreeRegister(getOverIndex, getOverRegister) + nsfwAlertEmitFingerprint.methodOrThrow().apply { + val hasBeenVisitedIndex = indexOfHasBeenVisitedInstruction(this) + val hasBeenVisitedRegister = + getInstruction(hasBeenVisitedIndex + 1).registerA - val returnIndex = indexOfFirstInstructionOrThrow(getOverIndex, Opcode.RETURN_OBJECT) - val jumpIndex = indexOfFirstInstructionReversedOrThrow(returnIndex, Opcode.SGET_OBJECT) - - addInstructionsWithLabels( - getOverIndex, """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->removeNSFWDialog()Z - move-result v$freeRegister - if-nez v$freeRegister, :jump - """, ExternalLabel("jump", getInstruction(jumpIndex)) - ) - } + addInstructions( + hasBeenVisitedIndex + 2, """ + invoke-static {v$hasBeenVisitedRegister}, $EXTENSION_CLASS_DESCRIPTOR->spoofHasBeenVisitedStatus(Z)Z + move-result v$hasBeenVisitedRegister + """ + ) } var hookCount = 0 From bb5964ce9800bb9b6444e212efbe8cf86b902eb4 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:51:58 +0900 Subject: [PATCH 12/26] fix(Reddit - Disable screenshot popup): Screenshot popup not being completely removed https://github.com/inotia00/ReVanced_Extended/issues/1810 --- .../reddit/patches/ScreenshotPopupPatch.java | 4 +- .../layout/screenshotpopup/Fingerprints.kt | 60 ------- .../screenshotpopup/ScreenshotPopupPatch.kt | 154 +++++------------- .../utils/resourceid/SharedResourceIdPatch.kt | 6 - 4 files changed, 42 insertions(+), 182 deletions(-) delete mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt diff --git a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/ScreenshotPopupPatch.java b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/ScreenshotPopupPatch.java index 7216ea55c..9c84cdbec 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/ScreenshotPopupPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/reddit/patches/ScreenshotPopupPatch.java @@ -5,7 +5,7 @@ import app.revanced.extension.reddit.settings.Settings; @SuppressWarnings("unused") public class ScreenshotPopupPatch { - public static boolean disableScreenshotPopup() { - return Settings.DISABLE_SCREENSHOT_POPUP.get(); + public static Boolean disableScreenshotPopup(Boolean original) { + return Settings.DISABLE_SCREENSHOT_POPUP.get() ? Boolean.FALSE : original; } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt deleted file mode 100644 index 42d350793..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/Fingerprints.kt +++ /dev/null @@ -1,60 +0,0 @@ -package app.revanced.patches.reddit.layout.screenshotpopup - -import app.revanced.patches.reddit.utils.resourceid.actionShare -import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.fingerprint.legacyFingerprint -import app.revanced.util.or -import com.android.tools.smali.dexlib2.AccessFlags - -internal val screenshotBannerContainerFingerprint = legacyFingerprint( - name = "screenshotTakenBannerFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - strings = listOf( - "bannerContainer", - "scope", - ) -) - -/** - * Reddit 2025.06.0 ~ - */ -internal val screenshotTakenBannerComposableFingerprint = legacyFingerprint( - name = "screenshotTakenBannerComposableFingerprint", - returnType = "L", - customFingerprint = { method, classDef -> - method.containsLiteralInstruction(actionShare) && - method.containsLiteralInstruction(screenShotShareBanner) && - classDef.type.startsWith("Lcom/reddit/sharing/screenshot/composables/") && - method.name == "invoke" - } -) - -/** - * ~ Reddit 2025.05.1 - */ -internal val screenshotTakenBannerLambdaActionFingerprint = legacyFingerprint( - name = "screenshotTakenBannerLambdaFingerprint", - returnType = "V", - parameters = listOf("Landroidx/compose/runtime/", "I"), - customFingerprint = { method, _ -> - method.containsLiteralInstruction(actionShare) && - method.name == "invoke" - } -) - -/** - * ~ Reddit 2025.05.1 - */ -internal val screenshotTakenBannerLambdaBannerFingerprint = legacyFingerprint( - name = "screenshotTakenBannerLambdaFingerprint", - returnType = "V", - parameters = listOf("Landroidx/compose/runtime/", "I"), - customFingerprint = { method, _ -> - method.containsLiteralInstruction(screenShotShareBanner) && - method.name == "invoke" - } -) - - diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt index 3b43169b2..4c4653142 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt @@ -1,37 +1,22 @@ package app.revanced.patches.reddit.layout.screenshotpopup -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH import app.revanced.patches.reddit.utils.patch.PatchList.DISABLE_SCREENSHOT_POPUP -import app.revanced.patches.reddit.utils.resourceid.actionShare -import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner -import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch -import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater import app.revanced.patches.reddit.utils.settings.settingsPatch import app.revanced.patches.reddit.utils.settings.updatePatchStatus import app.revanced.util.findMutableMethodOf -import app.revanced.util.fingerprint.methodCall -import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow -import app.revanced.util.indexOfFirstStringInstruction 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.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -private const val EXTENSION_METHOD_DESCRIPTOR = - "$PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup()Z" - @Suppress("unused") val screenshotPopupPatch = bytecodePatch( DISABLE_SCREENSHOT_POPUP.title, @@ -39,121 +24,62 @@ val screenshotPopupPatch = bytecodePatch( ) { compatibleWith(COMPATIBLE_PACKAGE) - dependsOn( - settingsPatch, - sharedResourceIdPatch, - ) + dependsOn(settingsPatch) execute { - val screenshotTriggerSharingListenerMethodCall = - screenshotBannerContainerFingerprint.methodCall() - - fun indexOfScreenshotTriggerInstruction(method: Method) = + fun indexOfShowBannerInstruction(method: Method) = method.indexOfFirstInstruction { - getReference()?.toString() == screenshotTriggerSharingListenerMethodCall + val reference = getReference() + opcode == Opcode.IGET_OBJECT && + reference?.name?.contains("shouldShowBanner") == true && + reference.definingClass.startsWith("Lcom/reddit/sharing/screenshot/") == true } - val isScreenshotTriggerMethod: Method.() -> Boolean = { - indexOfScreenshotTriggerInstruction(this) >= 0 - } - - var hookCount = 0 - - fun MutableMethod.hook() { - if (returnType == "V") { - addInstructionsWithLabels( - 0, """ - invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR - move-result v0 - if-eqz v0, :shown - return-void - """, ExternalLabel("shown", getInstruction(0)) - ) - - hookCount++ - } else if (returnType.startsWith("L")) { // Reddit 2025.06+ - val insertIndex = - indexOfFirstStringInstruction("screenshotTriggerSharingListener") - - if (insertIndex >= 0) { - val insertRegister = - getInstruction(insertIndex).registerA - val triggerIndex = - indexOfScreenshotTriggerInstruction(this) - val jumpIndex = - indexOfFirstInstructionOrThrow(triggerIndex, Opcode.RETURN_OBJECT) - - addInstructionsWithLabels( - insertIndex, """ - invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR - move-result v$insertRegister - if-nez v$insertRegister, :hidden - """, ExternalLabel("hidden", getInstruction(jumpIndex)) - ) - - hookCount++ - } + fun indexOfSetValueInstruction(method: Method) = + method.indexOfFirstInstruction { + getReference()?.name == "setValue" } - } - screenshotBannerContainerFingerprint - .methodOrThrow() - .hook() + fun indexOfBooleanInstruction(method: Method, startIndex: Int = 0) = + method.indexOfFirstInstruction(startIndex) { + val reference = getReference() + opcode == Opcode.SGET_OBJECT && + reference?.definingClass == "Ljava/lang/Boolean;" && + reference.type == "Ljava/lang/Boolean;" + } + + val isScreenShotMethod: Method.() -> Boolean = { + definingClass.startsWith("Lcom/reddit/sharing/screenshot/") && + name == "invokeSuspend" && + indexOfShowBannerInstruction(this) >= 0 && + indexOfBooleanInstruction(this) >= 0 && + indexOfSetValueInstruction(this) >= 0 + } classes.forEach { classDef -> classDef.methods.forEach { method -> - if (method.isScreenshotTriggerMethod()) { + if (method.isScreenShotMethod()) { proxy(classDef) .mutableClass .findMutableMethodOf(method) - .hook() + .apply { + val showBannerIndex = indexOfShowBannerInstruction(this) + val booleanIndex = indexOfBooleanInstruction(this, showBannerIndex) + val booleanRegister = + getInstruction(booleanIndex).registerA + + addInstructions( + booleanIndex + 1, """ + invoke-static {v$booleanRegister}, $PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup(Ljava/lang/Boolean;)Ljava/lang/Boolean; + move-result-object v$booleanRegister + """ + ) + } } } } - if (hookCount == 0) { - throw PatchException("Failed to find hook method") - } - - if (is_2025_06_or_greater) { - screenshotTakenBannerComposableFingerprint.methodOrThrow().apply { - arrayOf( - actionShare, - screenShotShareBanner - ).forEach { literal -> - val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) - val insertIndex = indexOfFirstInstructionReversedOrThrow(literalIndex, Opcode.CONST_4) - val insertRegister = getInstruction(insertIndex).registerA - val jumpIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.SGET_OBJECT) - - addInstructionsWithLabels( - insertIndex, """ - invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR - move-result v$insertRegister - if-nez v$insertRegister, :hidden - """, ExternalLabel("hidden", getInstruction(jumpIndex)) - ) - } - } - } else { - arrayOf( - screenshotTakenBannerLambdaActionFingerprint, - screenshotTakenBannerLambdaBannerFingerprint - ).forEach { fingerprint -> - fingerprint.methodOrThrow().addInstructionsWithLabels( - 0, """ - invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR - move-result v0 - if-eqz v0, :ignore - return-void - :ignore - nop - """ - ) - } - } - updatePatchStatus( "enableScreenshotPopup", DISABLE_SCREENSHOT_POPUP diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt index cb0ec3341..532b4326c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatch.kt @@ -5,12 +5,8 @@ import app.revanced.patches.shared.mapping.ResourceType.STRING import app.revanced.patches.shared.mapping.getResourceId import app.revanced.patches.shared.mapping.resourceMappingPatch -var actionShare = -1L - private set var nsfwDialogTitle = -1L private set -var screenShotShareBanner = -1L - private set internal val sharedResourceIdPatch = resourcePatch( description = "sharedResourceIdPatch" @@ -18,8 +14,6 @@ internal val sharedResourceIdPatch = resourcePatch( dependsOn(resourceMappingPatch) execute { - actionShare = getResourceId(STRING, "action_share") nsfwDialogTitle = getResourceId(STRING, "nsfw_dialog_title") - screenShotShareBanner = getResourceId(STRING, "screenshot_share_banner_title") } } \ No newline at end of file From 7e4a71b385c3c9098905686cbd61611b21dfa642 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:59:47 +0900 Subject: [PATCH 13/26] feat(GmsCore support): Add patch option `Patch all manifest components` --- .../patches/shared/gms/Fingerprints.kt | 5 + .../patches/shared/gms/GmsCoreSupportPatch.kt | 422 +++++++++++++++--- 2 files changed, 370 insertions(+), 57 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/gms/Fingerprints.kt index 1e79c58e2..3f2339393 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/gms/Fingerprints.kt @@ -106,3 +106,8 @@ internal val primesLifecycleEventFingerprint = legacyFingerprint( } >= 0 } ) + +internal val primeMethodFingerprint = legacyFingerprint( + name = "primesLifecycleEventFingerprint", + strings = listOf("com.google.android.GoogleCamera", "com.android.vending") +) \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/gms/GmsCoreSupportPatch.kt index 9fb601010..f82dd143a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/gms/GmsCoreSupportPatch.kt @@ -4,6 +4,7 @@ import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext @@ -18,9 +19,13 @@ import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.stringOption import app.revanced.patches.shared.extension.Constants.PATCHES_PATH import app.revanced.patches.shared.gms.Constants.ACTIONS +import app.revanced.patches.shared.gms.Constants.ACTIONS_LEGACY import app.revanced.patches.shared.gms.Constants.AUTHORITIES +import app.revanced.patches.shared.gms.Constants.AUTHORITIES_LEGACY import app.revanced.patches.shared.gms.Constants.PERMISSIONS +import app.revanced.patches.shared.gms.Constants.PERMISSIONS_LEGACY import app.revanced.util.Utils.trimIndentMultiline +import app.revanced.util.fingerprint.methodOrNull import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference @@ -127,6 +132,14 @@ fun gmsCoreSupportPatch( required = true ) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) && it != ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC } + val patchAllManifest by booleanOption( + key = "patchAllManifest", + default = true, + title = "Patch all manifest components", + description = "Patch all permissions, intents and content provider authorities supported by GmsCore.", + required = true, + ) + dependsOn( gmsCoreSupportResourcePatchFactory( gmsCoreVendorGroupIdOption, @@ -139,6 +152,20 @@ fun gmsCoreSupportPatch( val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption execute { + val patchAllManifestEnabled = patchAllManifest == true + val permissions = if (patchAllManifestEnabled) + PERMISSIONS + else + PERMISSIONS_LEGACY + val actions = if (patchAllManifestEnabled) + ACTIONS + else + ACTIONS_LEGACY + val authorities = if (patchAllManifestEnabled) + AUTHORITIES + else + AUTHORITIES_LEGACY + fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach { val mutableClass by lazy { proxy(it).mutableClass @@ -182,9 +209,9 @@ fun gmsCoreSupportPatch( when (referencedString) { "com.google", "com.google.android.gms", - in PERMISSIONS, - in ACTIONS, - in AUTHORITIES, + in permissions, + in actions, + in authorities, -> referencedString.replace("com.google", gmsCoreVendorGroupId!!) // TODO: Add this permission when bumping GmsCore @@ -196,7 +223,7 @@ fun gmsCoreSupportPatch( // only when content:// uri if (str.startsWith("content://")) { // check if matches any authority - for (authority in AUTHORITIES) { + for (authority in authorities) { val uriPrefix = "content://$authority" if (str.startsWith(uriPrefix)) { return str.replace( @@ -223,40 +250,56 @@ fun gmsCoreSupportPatch( } } - fun transformPrimeMethod() { - setOf( - primesBackgroundInitializationFingerprint, - primesLifecycleEventFingerprint - ).forEach { fingerprint -> - fingerprint.methodOrThrow().apply { - val exceptionIndex = indexOfFirstInstructionReversedOrThrow { - opcode == Opcode.NEW_INSTANCE && - (this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;" + fun transformPrimeMethod(packageName: String) { + if (patchAllManifestEnabled) { + primeMethodFingerprint.methodOrNull()?.apply { + var register = 2 + + val index = instructions.indexOfFirst { + if (it.getReference()?.string != fromPackageName) return@indexOfFirst false + + register = (it as OneRegisterInstruction).registerA + return@indexOfFirst true } - val index = - indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ) - val register = getInstruction(index).registerA - addInstruction( - index, - "const/4 v$register, 0x1" - ) + + replaceInstruction(index, "const-string v$register, \"$packageName\"") } - } - primesApiFingerprint.mutableClassOrThrow().methods.filter { method -> - method.name != "" && - method.returnType == "V" - }.forEach { method -> - method.apply { - val index = if (MethodUtil.isConstructor(method)) - indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_DIRECT && - getReference()?.name == "" - } + 1 - else 0 - addInstruction( - index, - "return-void" - ) + } else { + setOf( + primesBackgroundInitializationFingerprint, + primesLifecycleEventFingerprint + ).forEach { fingerprint -> + fingerprint.methodOrThrow().apply { + val exceptionIndex = indexOfFirstInstructionReversedOrThrow { + opcode == Opcode.NEW_INSTANCE && + (this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;" + } + val index = + indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ) + val register = getInstruction(index).registerA + addInstruction( + index, + "const/4 v$register, 0x1" + ) + } + } + + primesApiFingerprint.mutableClassOrThrow().methods.filter { method -> + method.name != "" && + method.returnType == "V" + }.forEach { method -> + method.apply { + val index = if (MethodUtil.isConstructor(method)) + indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_DIRECT && + getReference()?.name == "" + } + 1 + else 0 + addInstruction( + index, + "return-void" + ) + } } } } @@ -280,37 +323,40 @@ fun gmsCoreSupportPatch( return@transform null } - // Return these methods early to prevent the app from crashing. - setOf( + val earlyReturnFingerprints = mutableListOf( castContextFetchFingerprint, - castDynamiteModuleFingerprint, - castDynamiteModuleV2Fingerprint, googlePlayUtilityFingerprint, - serviceCheckFingerprint, - sslGuardFingerprint, - ).forEach { it.methodOrThrow().returnEarly() } + serviceCheckFingerprint + ) - // Prevent spam logs. - eCatcherFingerprint.methodOrThrow().apply { - val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY) - addInstruction(index, "return-void") + if (patchAllManifestEnabled) { + earlyReturnFingerprints += listOf(sslGuardFingerprint) + + // Prevent spam logs. + eCatcherFingerprint.methodOrThrow().apply { + val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY) + addInstruction(index, "return-void") + } } + // Return these methods early to prevent the app from crashing. + earlyReturnFingerprints.forEach { it.methodOrThrow().returnEarly() } + // Specific method that needs to be patched. - transformPrimeMethod() + transformPrimeMethod(packageName) // Verify GmsCore is installed and whitelisted for power optimizations and background usage. - mainActivityOnCreateFingerprint.method.apply { - // Temporary fix for patches with an extension patch that hook the onCreate method as well. - val setContextIndex = indexOfFirstInstruction { - val reference = - getReference() ?: return@indexOfFirstInstruction false + if (checkGmsCore == true) { + mainActivityOnCreateFingerprint.method.apply { + // Temporary fix for patches with an extension patch that hook the onCreate method as well. + val setContextIndex = indexOfFirstInstruction { + val reference = + getReference() ?: return@indexOfFirstInstruction false - reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V" - } + reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V" + } - // Add after setContext call, because this patch needs the context. - if (checkGmsCore == true) { + // Add after setContext call, because this patch needs the context. addInstructions( if (setContextIndex < 0) 0 else setContextIndex + 1, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + @@ -335,7 +381,30 @@ fun gmsCoreSupportPatch( * that are present in GmsCore which need to be transformed. */ private object Constants { + /** + * All permissions. + */ val PERMISSIONS = setOf( + "com.google.android.c2dm.permission.RECEIVE", + "com.google.android.c2dm.permission.SEND", + "com.google.android.gms.auth.api.phone.permission.SEND", + "com.google.android.gms.permission.AD_ID", + "com.google.android.gms.permission.AD_ID_NOTIFICATION", + "com.google.android.gms.permission.CAR_FUEL", + "com.google.android.gms.permission.CAR_INFORMATION", + "com.google.android.gms.permission.CAR_MILEAGE", + "com.google.android.gms.permission.CAR_SPEED", + "com.google.android.gms.permission.CAR_VENDOR_EXTENSION", + "com.google.android.googleapps.permission.GOOGLE_AUTH", + "com.google.android.googleapps.permission.GOOGLE_AUTH.cp", + "com.google.android.googleapps.permission.GOOGLE_AUTH.local", + "com.google.android.googleapps.permission.GOOGLE_AUTH.mail", + "com.google.android.googleapps.permission.GOOGLE_AUTH.writely", + "com.google.android.gtalkservice.permission.GTALK_SERVICE", + "com.google.android.providers.gsf.permission.READ_GSERVICES", + ) + + val PERMISSIONS_LEGACY = setOf( // C2DM / GCM "com.google.android.c2dm.permission.RECEIVE", "com.google.android.c2dm.permission.SEND", @@ -349,7 +418,234 @@ private object Constants { // "com.google.android.gms.permission.ACTIVITY_RECOGNITION", ) + /** + * All intent actions. + */ val ACTIONS = setOf( + "com.google.android.c2dm.intent.RECEIVE", + "com.google.android.c2dm.intent.REGISTER", + "com.google.android.c2dm.intent.REGISTRATION", + "com.google.android.c2dm.intent.UNREGISTER", + "com.google.android.contextmanager.service.ContextManagerService.START", + "com.google.android.gcm.intent.SEND", + "com.google.android.gms.accounts.ACCOUNT_SERVICE", + "com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS", + "com.google.android.gms.accountsettings.action.BROWSE_SETTINGS", + "com.google.android.gms.accountsettings.action.VIEW_SETTINGS", + "com.google.android.gms.accountsettings.MY_ACCOUNT", + "com.google.android.gms.accountsettings.PRIVACY_SETTINGS", + "com.google.android.gms.accountsettings.SECURITY_SETTINGS", + "com.google.android.gms.ads.gservice.START", + "com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION", + "com.google.android.gms.ads.service.CACHE", + "com.google.android.gms.ads.service.CONSENT_LOOKUP", + "com.google.android.gms.ads.service.HTTP", + "com.google.android.gms.analytics.service.START", + "com.google.android.gms.app.settings.GoogleSettingsLink", + "com.google.android.gms.appstate.service.START", + "com.google.android.gms.appusage.service.START", + "com.google.android.gms.asterism.service.START", + "com.google.android.gms.audiomodem.service.AudioModemService.START", + "com.google.android.gms.audit.service.START", + "com.google.android.gms.auth.account.authapi.START", + "com.google.android.gms.auth.account.authenticator.auto.service.START", + "com.google.android.gms.auth.account.authenticator.chromeos.START", + "com.google.android.gms.auth.account.authenticator.tv.service.START", + "com.google.android.gms.auth.account.data.service.START", + "com.google.android.gms.auth.api.credentials.PICKER", + "com.google.android.gms.auth.api.credentials.service.START", + "com.google.android.gms.auth.api.identity.service.authorization.START", + "com.google.android.gms.auth.api.identity.service.credentialsaving.START", + "com.google.android.gms.auth.api.identity.service.signin.START", + "com.google.android.gms.auth.api.phone.service.InternalService.START", + "com.google.android.gms.auth.api.signin.service.START", + "com.google.android.gms.auth.be.appcert.AppCertService", + "com.google.android.gms.auth.blockstore.service.START", + "com.google.android.gms.auth.config.service.START", + "com.google.android.gms.auth.cryptauth.cryptauthservice.START", + "com.google.android.gms.auth.GOOGLE_SIGN_IN", + "com.google.android.gms.auth.login.LOGIN", + "com.google.android.gms.auth.proximity.devicesyncservice.START", + "com.google.android.gms.auth.proximity.securechannelservice.START", + "com.google.android.gms.auth.proximity.START", + "com.google.android.gms.auth.service.START", + "com.google.android.gms.backup.ACTION_BACKUP_SETTINGS", + "com.google.android.gms.backup.G1_BACKUP", + "com.google.android.gms.backup.G1_RESTORE", + "com.google.android.gms.backup.GMS_MODULE_RESTORE", + "com.google.android.gms.beacon.internal.IBleService.START", + "com.google.android.gms.car.service.START", + "com.google.android.gms.carrierauth.service.START", + "com.google.android.gms.cast.firstparty.START", + "com.google.android.gms.cast.remote_display.service.START", + "com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE", + "com.google.android.gms.cast_mirroring.service.START", + "com.google.android.gms.checkin.BIND_TO_SERVICE", + "com.google.android.gms.chromesync.service.START", + "com.google.android.gms.clearcut.service.START", + "com.google.android.gms.common.account.CHOOSE_ACCOUNT", + "com.google.android.gms.common.download.START", + "com.google.android.gms.common.service.START", + "com.google.android.gms.common.telemetry.service.START", + "com.google.android.gms.config.START", + "com.google.android.gms.constellation.service.START", + "com.google.android.gms.credential.manager.service.firstparty.START", + "com.google.android.gms.deviceconnection.service.START", + "com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT", + "com.google.android.gms.drive.ApiService.START", + "com.google.android.gms.drive.ApiService.STOP", + "com.google.android.gms.droidguard.service.INIT", + "com.google.android.gms.droidguard.service.PING", + "com.google.android.gms.droidguard.service.START", + "com.google.android.gms.enterprise.loader.service.START", + "com.google.android.gms.facs.cache.service.START", + "com.google.android.gms.facs.internal.service.START", + "com.google.android.gms.feedback.internal.IFeedbackService", + "com.google.android.gms.fido.credentialstore.internal_service.START", + "com.google.android.gms.fido.fido2.privileged.START", + "com.google.android.gms.fido.fido2.regular.START", + "com.google.android.gms.fido.fido2.zeroparty.START", + "com.google.android.gms.fido.sourcedevice.service.START", + "com.google.android.gms.fido.targetdevice.internal_service.START", + "com.google.android.gms.fido.u2f.privileged.START", + "com.google.android.gms.fido.u2f.thirdparty.START", + "com.google.android.gms.fido.u2f.zeroparty.START", + "com.google.android.gms.fitness.BleApi", + "com.google.android.gms.fitness.ConfigApi", + "com.google.android.gms.fitness.GoalsApi", + "com.google.android.gms.fitness.GoogleFitnessService.START", + "com.google.android.gms.fitness.HistoryApi", + "com.google.android.gms.fitness.InternalApi", + "com.google.android.gms.fitness.RecordingApi", + "com.google.android.gms.fitness.SensorsApi", + "com.google.android.gms.fitness.SessionsApi", + "com.google.android.gms.fonts.service.START", + "com.google.android.gms.freighter.service.START", + "com.google.android.gms.games.internal.connect.service.START", + "com.google.android.gms.games.PLAY_GAMES_UPGRADE", + "com.google.android.gms.games.service.START", + "com.google.android.gms.gass.START", + "com.google.android.gms.gmscompliance.service.START", + "com.google.android.gms.googlehelp.HELP", + "com.google.android.gms.googlehelp.service.GoogleHelpService.START", + "com.google.android.gms.growth.service.START", + "com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START", + "com.google.android.gms.icing.INDEX_SERVICE", + "com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE", + "com.google.android.gms.identity.service.BIND", + "com.google.android.gms.inappreach.service.START", + "com.google.android.gms.instantapps.START", + "com.google.android.gms.kids.service.START", + "com.google.android.gms.languageprofile.service.START", + "com.google.android.gms.learning.internal.dynamitesupport.START", + "com.google.android.gms.learning.intservice.START", + "com.google.android.gms.learning.predictor.START", + "com.google.android.gms.learning.trainer.START", + "com.google.android.gms.learning.training.background.START", + "com.google.android.gms.location.places.GeoDataApi", + "com.google.android.gms.location.places.PlaceDetectionApi", + "com.google.android.gms.location.places.PlacesApi", + "com.google.android.gms.location.reporting.service.START", + "com.google.android.gms.location.settings.LOCATION_HISTORY", + "com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS", + "com.google.android.gms.locationsharing.api.START", + "com.google.android.gms.locationsharingreporter.service.START", + "com.google.android.gms.lockbox.service.START", + "com.google.android.gms.matchstick.lighter.service.START", + "com.google.android.gms.mdm.services.DeviceManagerApiService.START", + "com.google.android.gms.mdm.services.START", + "com.google.android.gms.mdns.service.START", + "com.google.android.gms.measurement.START", + "com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START", + "com.google.android.gms.nearby.connection.service.START", + "com.google.android.gms.nearby.fastpair.START", + "com.google.android.gms.nearby.messages.service.NearbyMessagesService.START", + "com.google.android.gms.nearby.sharing.service.NearbySharingService.START", + "com.google.android.gms.nearby.sharing.START_SERVICE", + "com.google.android.gms.notifications.service.START", + "com.google.android.gms.ocr.service.internal.START", + "com.google.android.gms.ocr.service.START", + "com.google.android.gms.oss.licenses.service.START", + "com.google.android.gms.payse.service.BIND", + "com.google.android.gms.people.contactssync.service.START", + "com.google.android.gms.people.service.START", + "com.google.android.gms.phenotype.service.START", + "com.google.android.gms.photos.autobackup.service.START", + "com.google.android.gms.playlog.service.START", + "com.google.android.gms.plus.service.default.INTENT", + "com.google.android.gms.plus.service.image.INTENT", + "com.google.android.gms.plus.service.internal.START", + "com.google.android.gms.plus.service.START", + "com.google.android.gms.potokens.service.START", + "com.google.android.gms.pseudonymous.service.START", + "com.google.android.gms.rcs.START", + "com.google.android.gms.reminders.service.START", + "com.google.android.gms.romanesco.MODULE_BACKUP_AGENT", + "com.google.android.gms.romanesco.service.START", + "com.google.android.gms.safetynet.service.START", + "com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE", + "com.google.android.gms.search.service.SEARCH_AUTH_START", + "com.google.android.gms.semanticlocation.service.START_ODLH", + "com.google.android.gms.sesame.service.BIND", + "com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS", + "com.google.android.gms.setup.auth.SecondDeviceAuth.START", + "com.google.android.gms.signin.service.START", + "com.google.android.gms.smartdevice.d2d.SourceDeviceService.START", + "com.google.android.gms.smartdevice.d2d.TargetDeviceService.START", + "com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START", + "com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START", + "com.google.android.gms.smartdevice.postsetup.PostSetupService.START", + "com.google.android.gms.smartdevice.setup.accounts.AccountsService.START", + "com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE", + "com.google.android.gms.social.location.activity.service.START", + "com.google.android.gms.speech.service.START", + "com.google.android.gms.statementservice.EXECUTE", + "com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES", + "com.google.android.gms.tapandpay.service.BIND", + "com.google.android.gms.telephonyspam.service.START", + "com.google.android.gms.testsupport.service.START", + "com.google.android.gms.thunderbird.service.START", + "com.google.android.gms.trustagent.BridgeApi.START", + "com.google.android.gms.trustagent.StateApi.START", + "com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND", + "com.google.android.gms.trustlet.bluetooth.service.BIND", + "com.google.android.gms.trustlet.connectionlessble.service.BIND", + "com.google.android.gms.trustlet.face.service.BIND", + "com.google.android.gms.trustlet.nfc.service.BIND", + "com.google.android.gms.trustlet.onbody.service.BIND", + "com.google.android.gms.trustlet.place.service.BIND", + "com.google.android.gms.trustlet.voiceunlock.service.BIND", + "com.google.android.gms.udc.service.START", + "com.google.android.gms.update.START_API_SERVICE", + "com.google.android.gms.update.START_SERVICE", + "com.google.android.gms.update.START_SINGLE_USER_API_SERVICE", + "com.google.android.gms.update.START_TV_API_SERVICE", + "com.google.android.gms.usagereporting.service.START", + "com.google.android.gms.userlocation.service.START", + "com.google.android.gms.vehicle.cabin.service.START", + "com.google.android.gms.vehicle.climate.service.START", + "com.google.android.gms.vehicle.info.service.START", + "com.google.android.gms.wallet.service.BIND", + "com.google.android.gms.walletp2p.service.firstparty.BIND", + "com.google.android.gms.walletp2p.service.zeroparty.BIND", + "com.google.android.gms.wearable.BIND", + "com.google.android.gms.wearable.BIND_LISTENER", + "com.google.android.gms.wearable.DATA_CHANGED", + "com.google.android.gms.wearable.MESSAGE_RECEIVED", + "com.google.android.gms.wearable.NODE_CHANGED", + "com.google.android.gsf.action.GET_GLS", + "com.google.android.location.settings.LOCATION_REPORTING_SETTINGS", + "com.google.android.mdd.service.START", + "com.google.android.mdh.service.listener.START", + "com.google.android.mdh.service.START", + "com.google.android.mobstore.service.START", + "com.google.firebase.auth.api.gms.service.START", + "com.google.firebase.dynamiclinks.service.START", + "com.google.iid.TOKEN_REQUEST", + "com.google.android.gms.location.places.ui.PICK_PLACE", + ) + + val ACTIONS_LEGACY = setOf( // C2DM / GCM "com.google.android.c2dm.intent.REGISTER", "com.google.android.c2dm.intent.REGISTRATION", @@ -407,7 +703,19 @@ private object Constants { "com.google.android.gms.droidguard.service.START", ) + /** + * All content provider authorities. + */ val AUTHORITIES = setOf( + "com.google.android.gms.auth.accounts", + "com.google.android.gms.chimera", + "com.google.android.gms.fonts", + "com.google.android.gms.phenotype", + "com.google.android.gsf.gservices", + "com.google.settings", + ) + + val AUTHORITIES_LEGACY = setOf( // gsf "com.google.android.gsf.gservices", From 22b98336d550510a71ee38face8d10d0ee464f9d Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:00:18 +0900 Subject: [PATCH 14/26] fix(YouTube - Custom branding icon): Splash animation background color is always white https://github.com/inotia00/ReVanced_Extended/issues/2892 --- .../branding/icon/CustomBrandingIconPatch.kt | 15 ++--- .../fix/splash/DarkModeSplashScreenPatch.kt | 66 +++++++++---------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt index e954f02b8..54f62a8e7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt @@ -218,8 +218,7 @@ val customBrandingIconPatch = resourcePatch( } val styleList = mutableListOf( - Triple( - "values-v31", + Pair( "Base.Theme.YouTube.Launcher", "@style/Theme.AppCompat.DayNight.NoActionBar" ), @@ -227,21 +226,15 @@ val customBrandingIconPatch = resourcePatch( if (is_19_32_or_greater) { styleList += listOf( - Triple( - "values-night-v31", - "Theme.YouTube.Home", - "@style/Base.V27.Theme.YouTube.Home" - ), - Triple( - "values-v31", + Pair( "Theme.YouTube.Home", "@style/Base.V27.Theme.YouTube.Home" ), ) } - styleList.forEach { (directory, nodeAttributeName, nodeAttributeParent) -> - document("res/$directory/styles.xml").use { document -> + styleList.forEach { (nodeAttributeName, nodeAttributeParent) -> + document("res/values-v31/styles.xml").use { document -> val resourcesNode = document.getElementsByTagName("resources").item(0) as Element diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt index f67961e91..5a193d9e6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/splash/DarkModeSplashScreenPatch.kt @@ -1,11 +1,9 @@ package app.revanced.patches.youtube.utils.fix.splash import app.revanced.patcher.patch.resourcePatch -import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch -import app.revanced.patches.youtube.utils.settings.ResourceUtils.youtubePackageName -import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.patches.youtube.utils.settings.ResourceUtils.restoreOldSplashAnimationIncluded import org.w3c.dom.Element /** @@ -22,16 +20,13 @@ val darkModeSplashScreenPatch = resourcePatch( ) { dependsOn(versionCheckPatch) - execute { + finalize { if (!is_19_32_or_greater) { - return@execute + return@finalize } - arrayOf( - "values-night", - "values-night-v27", - ).forEach { directory -> - document("res/$directory/styles.xml").use { document -> + if (restoreOldSplashAnimationIncluded) { + document("res/values-night/styles.xml").use { document -> val resourcesNode = document.getElementsByTagName("resources").item(0) as Element val childNodes = resourcesNode.childNodes @@ -45,38 +40,41 @@ val darkModeSplashScreenPatch = resourcePatch( style.setAttribute("name", "Theme.YouTube.Home") style.setAttribute("parent", nodeAttributeParent) - val colorSplashBackgroundColor = "@color/yt_black1" - arrayOf( - "android:navigationBarColor" to colorSplashBackgroundColor, - "android:windowBackground" to colorSplashBackgroundColor, - "android:colorBackground" to colorSplashBackgroundColor, - "colorPrimaryDark" to colorSplashBackgroundColor, - "android:windowLightStatusBar" to "false", - ).forEach { (name, value) -> - val styleItem = document.createElement("item") - styleItem.setAttribute("name", name) - styleItem.textContent = value - style.appendChild(styleItem) - } + val windowItem = document.createElement("item") + windowItem.setAttribute("name", "android:windowBackground") + windowItem.textContent = "@color/yt_black1" + style.appendChild(windowItem) resourcesNode.removeChild(node) resourcesNode.appendChild(style) } } } - } - } + } else { + document("res/values-night-v27/styles.xml").use { document -> + // Create a night mode specific override for the splash screen background. + val style = document.createElement("style") + style.setAttribute("name", "Theme.YouTube.Home") + style.setAttribute("parent", "@style/Base.V27.Theme.YouTube.Home") - finalize { - // GmsCore support included - if (youtubePackageName != YOUTUBE_PACKAGE_NAME) { - document("AndroidManifest.xml").use { document -> - val mainActivityElement = document.childNodes.findElementByAttributeValueOrThrow( - "android:name", - "com.google.android.apps.youtube.app.watchwhile.MainActivity", - ) + // Fix status and navigation bar showing white on some Android devices, + // such as SDK 28 Android 10 medium tablet. + val colorSplashBackgroundColor = "@color/yt_black1" + arrayOf( + "android:navigationBarColor" to colorSplashBackgroundColor, + "android:windowBackground" to colorSplashBackgroundColor, + "android:colorBackground" to colorSplashBackgroundColor, + "colorPrimaryDark" to colorSplashBackgroundColor, + "android:windowLightStatusBar" to "false", + ).forEach { (name, value) -> + val styleItem = document.createElement("item") + styleItem.setAttribute("name", name) + styleItem.textContent = value + style.appendChild(styleItem) + } - mainActivityElement.setAttribute("android:launchMode", "singleTask") + val resourcesNode = document.getElementsByTagName("resources").item(0) as Element + resourcesNode.appendChild(style) } } } From 457dbfec6c731ca998d11159b22fc60aaa091712 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:01:18 +0900 Subject: [PATCH 15/26] feat(YouTube - Disable resuming Shorts on startup): Match with ReVanced --- .../shorts/startupshortsreset/Fingerprints.kt | 34 ++-------- .../ResumingShortsOnStartupPatch.kt | 63 ++++--------------- 2 files changed, 18 insertions(+), 79 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/Fingerprints.kt index a8408ede6..fbe79271e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/Fingerprints.kt @@ -1,41 +1,17 @@ package app.revanced.patches.youtube.shorts.startupshortsreset import app.revanced.util.fingerprint.legacyFingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction import app.revanced.util.or 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 /** - * YouTube v18.15.40 ~ YouTube 19.46.42 + * YouTube v18.15.40+ */ -internal val userWasInShortsABConfigFingerprint = legacyFingerprint( +internal val userWasInShortsConfigFingerprint = legacyFingerprint( name = "userWasInShortsABConfigFingerprint", - returnType = "V", - strings = listOf("Failed to get offline response: "), - customFingerprint = { method, _ -> - indexOfOptionalInstruction(method) >= 0 - } -) - -internal fun indexOfOptionalInstruction(method: Method) = - method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_STATIC && - getReference().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;" - } - -/** - * YouTube 19.47.53 ~ - */ -internal val userWasInShortsABConfigAlternativeFingerprint = legacyFingerprint( - name = "userWasInShortsABConfigAlternativeFingerprint", - returnType = "V", - parameters = listOf("I"), - opcodes = listOf(Opcode.OR_INT_LIT8), - strings = listOf("alias", "null"), + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Z", + literals = listOf(45358360L) ) /** diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/ResumingShortsOnStartupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/ResumingShortsOnStartupPatch.kt index bfbc72f85..b28a77764 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/ResumingShortsOnStartupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/ResumingShortsOnStartupPatch.kt @@ -4,14 +4,10 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_RESUMING_SHORTS_ON_STARTUP -import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference @@ -19,10 +15,8 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.getReference -import app.revanced.util.getWalkerMethod import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow -import app.revanced.util.indexOfFirstStringInstructionOrThrow 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 @@ -42,50 +36,19 @@ val resumingShortsOnStartupPatch = bytecodePatch( execute { - fun MutableMethod.hookUserWasInShortsABConfig(startIndex: Int) { - val walkerIndex = implementation!!.instructions.let { - val subListIndex = - it.subList(startIndex, startIndex + 20).indexOfFirst { instruction -> - val reference = instruction.getReference() - instruction.opcode == Opcode.INVOKE_VIRTUAL && - reference?.returnType == "Z" && - reference.definingClass != "Lj${'$'}/util/Optional;" && - reference.parameterTypes.isEmpty() - } - if (subListIndex < 0) - throw PatchException("subListIndex not found") - - startIndex + subListIndex - } - val walkerMethod = getWalkerMethod(walkerIndex) - - // This method will only be called for the user being A/B tested. - // Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key. - walkerMethod.apply { - addInstructionsWithLabels( - 0, """ - invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z - move-result v0 - if-eqz v0, :show - const/4 v0, 0x0 - return v0 - """, ExternalLabel("show", getInstruction(0)) - ) - } - } - - if (is_19_46_or_greater) { - userWasInShortsABConfigAlternativeFingerprint.methodOrThrow().apply { - val stringIndex = indexOfFirstStringInstructionOrThrow("null") - val startIndex = indexOfFirstInstructionOrThrow(stringIndex, Opcode.OR_INT_LIT8) - hookUserWasInShortsABConfig(startIndex) - } - } else { - userWasInShortsABConfigFingerprint.methodOrThrow().apply { - val startIndex = indexOfOptionalInstruction(this) - hookUserWasInShortsABConfig(startIndex) - } - } + userWasInShortsConfigFingerprint + .methodOrThrow() + .addInstructionsWithLabels( + 0, """ + invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z + move-result v0 + if-eqz v0, :show + const/4 v0, 0x0 + return v0 + :show + nop + """ + ) if (is_20_02_or_greater) { userWasInShortsAlternativeFingerprint.matchOrThrow().let { From 64af7fd8b6f1f03cd1d15c55ae2519d747b91147 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:02:55 +0900 Subject: [PATCH 16/26] fix(YouTube - Video playback): Overridden to default playback speed in `onResume` callback https://github.com/inotia00/ReVanced_Extended/issues/2896 --- .../patches/video/PlaybackSpeedPatch.java | 131 +++++++++++------- .../utils/dismiss/DismissPlayerHookPatch.kt | 33 ++++- .../video/playback/VideoPlaybackPatch.kt | 6 +- 3 files changed, 119 insertions(+), 51 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/PlaybackSpeedPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/PlaybackSpeedPatch.java index 58897b35d..895a793a2 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/PlaybackSpeedPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/PlaybackSpeedPatch.java @@ -3,10 +3,14 @@ package app.revanced.extension.youtube.patches.video; import static app.revanced.extension.shared.utils.StringRef.str; import static app.revanced.extension.youtube.shared.RootView.isShortsActive; +import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import org.apache.commons.lang3.BooleanUtils; +import java.util.LinkedHashMap; +import java.util.Map; + import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.FloatSetting; import app.revanced.extension.shared.utils.Logger; @@ -28,48 +32,61 @@ public class PlaybackSpeedPatch { Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get(); private static final long TOAST_DELAY_MILLISECONDS = 750; private static long lastTimeSpeedChanged; + + /** + * The last used playback speed. + * This value is used when the default playback speed is 'Auto'. + */ private static float lastSelectedPlaybackSpeed = 1.0f; + private static float lastSelectedShortsPlaybackSpeed = 1.0f; - private static volatile String channelId = ""; - private static volatile String videoId = ""; - private static boolean isLiveStream; + /** + * The last regular video id. + */ + private static String videoId = ""; - private static volatile String channelIdShorts = ""; - private static volatile String videoIdShorts = ""; - private static boolean isLiveStreamShorts; + @GuardedBy("itself") + private static final Map ignoredPlaybackSpeedVideoIds = new LinkedHashMap<>() { + private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 3; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK; + } + }; /** * Injection point. + * This method is used to reset the playback speed to 1.0 when a general video is started, whether it is a live stream, music, or whitelist. */ public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName, @NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle, final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) { if (isShortsActive()) { - channelIdShorts = newlyLoadedChannelId; - videoIdShorts = newlyLoadedVideoId; - isLiveStreamShorts = newlyLoadedLiveStreamValue; - - Logger.printDebug(() -> "newVideoStarted: " + newlyLoadedVideoId); - } else { - channelId = newlyLoadedChannelId; - videoId = newlyLoadedVideoId; - isLiveStream = newlyLoadedLiveStreamValue; - - Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId); + return; } - } + if (videoId.equals(newlyLoadedVideoId)) { + return; + } + videoId = newlyLoadedVideoId; - /** - * Injection point. - */ - public static void newShortsVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName, - @NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle, - final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) { - channelIdShorts = newlyLoadedChannelId; - videoIdShorts = newlyLoadedVideoId; - isLiveStreamShorts = newlyLoadedLiveStreamValue; + boolean isMusic = isMusic(newlyLoadedVideoId); + boolean isWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(newlyLoadedVideoId); - Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId); + if (newlyLoadedLiveStreamValue || isMusic || isWhitelisted) { + synchronized(ignoredPlaybackSpeedVideoIds) { + if (!ignoredPlaybackSpeedVideoIds.containsKey(newlyLoadedVideoId)) { + lastSelectedPlaybackSpeed = 1.0f; + ignoredPlaybackSpeedVideoIds.put(newlyLoadedVideoId, lastSelectedPlaybackSpeed); + + VideoInformation.setPlaybackSpeed(lastSelectedPlaybackSpeed); + VideoInformation.overridePlaybackSpeed(lastSelectedPlaybackSpeed); + + Logger.printDebug(() -> "changing playback speed to: 1.0, isLiveStream: " + newlyLoadedLiveStreamValue + + ", isMusic: " + isMusic + ", isWhitelisted: " + isWhitelisted); + } + } + } } /** @@ -98,32 +115,29 @@ public class PlaybackSpeedPatch { /** * Injection point. + * This method is called every second for regular videos and Shorts. */ public static float getPlaybackSpeed(float playbackSpeed) { boolean isShorts = isShortsActive(); - String currentChannelId = isShorts ? channelIdShorts : channelId; - String currentVideoId = isShorts ? videoIdShorts : videoId; - boolean currentVideoIsLiveStream = isShorts ? isLiveStreamShorts : isLiveStream; - boolean currentVideoIsWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(currentChannelId); - boolean currentVideoIsMusic = !isShorts && isMusic(); - - if (currentVideoIsLiveStream || currentVideoIsWhitelisted || currentVideoIsMusic) { - Logger.printDebug(() -> "changing playback speed to: 1.0"); - VideoInformation.setPlaybackSpeed(1.0f); - return 1.0f; - } - float defaultPlaybackSpeed = isShorts ? DEFAULT_PLAYBACK_SPEED_SHORTS.get() : DEFAULT_PLAYBACK_SPEED.get(); - if (defaultPlaybackSpeed < 0) { - float finalPlaybackSpeed = isShorts ? playbackSpeed : lastSelectedPlaybackSpeed; + if (defaultPlaybackSpeed < 0) { // If the default playback speed is 'Auto', it will be overridden to the last used playback speed. + float finalPlaybackSpeed = isShorts ? lastSelectedShortsPlaybackSpeed : lastSelectedPlaybackSpeed; VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed); Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed); return finalPlaybackSpeed; - } else { - if (isShorts) { - VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed); + } else { // Otherwise the default playback speed is used. + synchronized (ignoredPlaybackSpeedVideoIds) { + if (isShorts) { + // For Shorts, the VideoInformation.overridePlaybackSpeed() method is not used, so manually save the playback speed in VideoInformation. + VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed); + } else if (ignoredPlaybackSpeedVideoIds.containsKey(videoId)) { + // For general videos, check whether the default video playback speed should not be applied. + Logger.printDebug(() -> "changing playback speed to: 1.0"); + return 1.0f; + } } + Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed); return defaultPlaybackSpeed; } @@ -138,6 +152,19 @@ public class PlaybackSpeedPatch { public static void userSelectedPlaybackSpeed(float playbackSpeed) { try { boolean isShorts = isShortsActive(); + + // Saves the user-selected playback speed in the method. + if (isShorts) { + lastSelectedShortsPlaybackSpeed = playbackSpeed; + } else { + lastSelectedPlaybackSpeed = playbackSpeed; + // If the user has manually changed the playback speed, the whitelist has already been applied. + // If there is a videoId on the map, it will be removed. + synchronized (ignoredPlaybackSpeedVideoIds) { + ignoredPlaybackSpeedVideoIds.remove(videoId); + } + } + if (PatchStatus.RememberPlaybackSpeed()) { BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts ? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED @@ -178,15 +205,23 @@ public class PlaybackSpeedPatch { } }, TOAST_DELAY_MILLISECONDS); } - } else if (!isShorts) { - lastSelectedPlaybackSpeed = playbackSpeed; } } catch (Exception ex) { Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex); } } - private static boolean isMusic() { + /** + * Injection point. + */ + public static void onDismiss() { + synchronized (ignoredPlaybackSpeedVideoIds) { + ignoredPlaybackSpeedVideoIds.remove(videoId); + videoId = ""; + } + } + + private static boolean isMusic(String videoId) { if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) { try { MusicRequest request = MusicRequest.getRequestForVideoId(videoId); diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt index fb390deff..45e71e6ef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt @@ -3,12 +3,14 @@ package app.revanced.patches.youtube.utils.dismiss import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.util.addStaticFieldToExtension import app.revanced.util.findMethodOrThrow import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.getReference +import app.revanced.util.getWalkerMethod import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow @@ -21,6 +23,8 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR = "$EXTENSION_PATH/utils/VideoUtils;" +private lateinit var dismissMethod: MutableMethod + val dismissPlayerHookPatch = bytecodePatch( description = "dismissPlayerHookPatch" ) { @@ -36,6 +40,21 @@ val dismissPlayerHookPatch = bytecodePatch( reference?.returnType == "V" && reference.parameterTypes.isEmpty() } + + getWalkerMethod(dismissPlayerIndex).apply { + val jumpIndex = indexOfFirstInstructionReversedOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.returnType == "V" + } + getWalkerMethod(jumpIndex).apply { + val jumpIndex = indexOfFirstInstructionReversedOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.returnType == "V" + } + dismissMethod = getWalkerMethod(jumpIndex) + } + } + val dismissPlayerReference = getInstruction(dismissPlayerIndex).reference as MethodReference val dismissPlayerClass = dismissPlayerReference.definingClass @@ -80,4 +99,16 @@ val dismissPlayerHookPatch = bytecodePatch( } } } -} \ No newline at end of file +} + +/** + * This method is called when the video is closed. + */ +internal fun hookDismissObserver(descriptor: String) = + dismissMethod.apply { + println("Class: $definingClass Name: $name") + addInstruction( + 0, + "invoke-static {}, $descriptor" + ) + } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt index 8052b44fd..faa0af987 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt @@ -11,6 +11,8 @@ import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE +import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch +import app.revanced.patches.youtube.utils.dismiss.hookDismissObserver import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.VIDEO_PATH @@ -25,7 +27,6 @@ import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.video.information.hookBackgroundPlayVideoInformation -import app.revanced.patches.youtube.video.information.hookShortsVideoInformation import app.revanced.patches.youtube.video.information.hookVideoInformation import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.speedSelectionInsertMethod @@ -87,6 +88,7 @@ val videoPlaybackPatch = bytecodePatch( ), flyoutMenuHookPatch, lithoFilterPatch, + dismissPlayerHookPatch, playerTypeHookPatch, recyclerViewTreeObserverPatch, shortsPlaybackPatch, @@ -183,9 +185,9 @@ val videoPlaybackPatch = bytecodePatch( } hookBackgroundPlayVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") - hookShortsVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newShortsVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") hookVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") hookPlayerResponseVideoId("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->fetchMusicRequest(Ljava/lang/String;Z)V") + hookDismissObserver("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->onDismiss()V") updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed") From e55fd4eb74ef0376f6f39629180ba5481e26d190 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:05:20 +0900 Subject: [PATCH 17/26] chore: Remove debug instruction --- .../youtube/utils/dismiss/DismissPlayerHookPatch.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt index 45e71e6ef..4545d4f8c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatch.kt @@ -105,10 +105,7 @@ val dismissPlayerHookPatch = bytecodePatch( * This method is called when the video is closed. */ internal fun hookDismissObserver(descriptor: String) = - dismissMethod.apply { - println("Class: $definingClass Name: $name") - addInstruction( - 0, - "invoke-static {}, $descriptor" - ) - } \ No newline at end of file + dismissMethod.addInstruction( + 0, + "invoke-static {}, $descriptor" + ) \ No newline at end of file From 4a19a960c5aa3f978d8b5aa3d5511c2fac150352 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:06:28 +0900 Subject: [PATCH 18/26] fix(YouTube - Settings): The default value for the patch option is incorrect --- .../revanced/patches/youtube/utils/settings/SettingsPatch.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt index 60cd26b83..e94490a05 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt @@ -100,7 +100,7 @@ private const val DEFAULT_ELEMENT = "@string/parent_tools_key" private const val DEFAULT_LABEL = "RVX" private val SETTINGS_ELEMENTS_MAP = mapOf( - "Parent settings" to "@string/parent_tools_key", + "Parent settings" to DEFAULT_ELEMENT, "General" to "@string/general_key", "Account" to "@string/account_switcher_key", "Data saving" to "@string/data_saving_settings_key", @@ -121,7 +121,7 @@ private val SETTINGS_ELEMENTS_MAP = mapOf( "Live chat" to "@string/live_chat_key", "Captions" to "@string/captions_key", "Accessibility" to "@string/accessibility_settings_key", - "About" to DEFAULT_ELEMENT + "About" to "@string/about_key" ) private lateinit var settingsLabel: String From 783e3662422675a4d6d492486da9fb14fa7fbada Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:09:19 +0900 Subject: [PATCH 19/26] fix(YouTube - Hook download actions, Overlay buttons): Auth key is set before the Context is initialized --- .../youtube/patches/utils/PlaylistPatch.java | 44 +++---------------- .../extension/youtube/utils/AuthUtils.java | 40 +++++++++++++++++ .../youtube/utils/auth/AuthHookPatch.kt | 34 ++++++++++++++ .../youtube/utils/auth/Fingerprints.kt | 14 ++++++ .../youtube/utils/playlist/Fingerprints.kt | 9 ---- .../youtube/utils/playlist/PlaylistPatch.kt | 16 +------ 6 files changed, 96 insertions(+), 61 deletions(-) create mode 100644 extensions/shared/src/main/java/app/revanced/extension/youtube/utils/AuthUtils.java create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/AuthHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/Fingerprints.kt diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java index 0e157be43..98ebe9f3c 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/PlaylistPatch.java @@ -1,6 +1,10 @@ package app.revanced.extension.youtube.patches.utils; import static app.revanced.extension.shared.utils.StringRef.str; +import static app.revanced.extension.shared.utils.Utils.runOnMainThreadDelayed; +import static app.revanced.extension.youtube.utils.VideoUtils.dismissPlayer; +import static app.revanced.extension.youtube.utils.VideoUtils.launchVideoExternalDownloader; +import static app.revanced.extension.youtube.utils.VideoUtils.openPlaylist; import android.content.Context; import android.view.KeyEvent; @@ -32,30 +36,18 @@ import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.VideoInformation; +import app.revanced.extension.youtube.utils.AuthUtils; import app.revanced.extension.youtube.utils.ExtendedUtils; -import app.revanced.extension.youtube.utils.VideoUtils; import kotlin.Pair; // TODO: Implement sync queue and clean up code. @SuppressWarnings({"unused", "StaticFieldLeak"}) -public class PlaylistPatch extends VideoUtils { - private static final String AUTHORIZATION_HEADER = "Authorization"; - private static final String[] REQUEST_HEADER_KEYS = { - AUTHORIZATION_HEADER, - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - }; +public class PlaylistPatch extends AuthUtils { private static final boolean QUEUE_MANAGER = Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get() || Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get(); private static Context mContext; - private static volatile String authorization = ""; - public static volatile String dataSyncId = ""; - public static volatile boolean isIncognito = false; - private static volatile Map requestHeader; - private static volatile String playlistId = ""; - private static volatile String videoId = ""; private static String checkFailedAuth; private static String checkFailedPlaylistId; @@ -141,30 +133,6 @@ public class PlaylistPatch extends VideoUtils { } } - /** - * Injection point. - */ - public static void setRequestHeaders(String url, Map requestHeaders) { - if (QUEUE_MANAGER) { - try { - // Save requestHeaders whenever an account is switched. - String auth = requestHeaders.get(AUTHORIZATION_HEADER); - if (auth == null || authorization.equals(auth)) { - return; - } - for (String key : REQUEST_HEADER_KEYS) { - if (requestHeaders.get(key) == null) { - return; - } - } - authorization = auth; - requestHeader = requestHeaders; - } catch (Exception ex) { - Logger.printException(() -> "setRequestHeaders failure", ex); - } - } - } - /** * Invoked by extension. */ diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/AuthUtils.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/AuthUtils.java new file mode 100644 index 000000000..8e015700e --- /dev/null +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/utils/AuthUtils.java @@ -0,0 +1,40 @@ +package app.revanced.extension.youtube.utils; + +import java.util.Map; + +import app.revanced.extension.shared.utils.Logger; + +@SuppressWarnings("unused") +public class AuthUtils { + public static final String AUTHORIZATION_HEADER = "Authorization"; + public static final String[] REQUEST_HEADER_KEYS = { + AUTHORIZATION_HEADER, + "X-GOOG-API-FORMAT-VERSION", + "X-Goog-Visitor-Id" + }; + public static volatile String authorization = ""; + public static volatile String dataSyncId = ""; + public static volatile boolean isIncognito = false; + public static volatile Map requestHeader; + public static volatile String playlistId = ""; + public static volatile String videoId = ""; + + public static void setRequestHeaders(String url, Map requestHeaders) { + try { + // Save requestHeaders whenever an account is switched. + String auth = requestHeaders.get(AUTHORIZATION_HEADER); + if (auth == null || authorization.equals(auth)) { + return; + } + for (String key : REQUEST_HEADER_KEYS) { + if (requestHeaders.get(key) == null) { + return; + } + } + authorization = auth; + requestHeader = requestHeaders; + } catch (Exception ex) { + Logger.initializationException(AuthUtils.class, "setRequestHeaders failure", ex); + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/AuthHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/AuthHookPatch.kt new file mode 100644 index 000000000..fe3e89272 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/AuthHookPatch.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.youtube.utils.auth + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH +import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch +import app.revanced.patches.youtube.utils.request.buildRequestPatch +import app.revanced.patches.youtube.utils.request.hookBuildRequest +import app.revanced.util.fingerprint.methodOrThrow + +private const val EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR = + "$EXTENSION_PATH/utils/AuthUtils;" + +val authHookPatch = bytecodePatch( + description = "authHookPatch" +) { + dependsOn( + sharedExtensionPatch, + buildRequestPatch, + ) + + execute { + // Get incognito status and data sync id. + accountIdentityFingerprint.methodOrThrow().addInstructions( + 1, """ + sput-object p3, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String; + sput-boolean p4, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->isIncognito:Z + """ + ) + + // Get the header to use the auth token. + hookBuildRequest("$EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V") + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/Fingerprints.kt new file mode 100644 index 000000000..85678373a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/auth/Fingerprints.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.utils.auth + +import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.or +import com.android.tools.smali.dexlib2.AccessFlags + +internal val accountIdentityFingerprint = legacyFingerprint( + name = "accountIdentityFingerprint", + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + customFingerprint = { method, _ -> + method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;") + } +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/Fingerprints.kt index 10c9e073d..9a55e8ecf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/Fingerprints.kt @@ -9,15 +9,6 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.reference.FieldReference -internal val accountIdentityFingerprint = legacyFingerprint( - name = "accountIdentityFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, - customFingerprint = { method, _ -> - method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;") - } -) - internal val editPlaylistConstructorFingerprint = legacyFingerprint( name = "editPlaylistConstructorFingerprint", returnType = "V", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt index 9f23ad2db..8d9fa6fb8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playlist/PlaylistPatch.kt @@ -7,13 +7,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.mainactivity.getMainActivityMethod +import app.revanced.patches.youtube.utils.auth.authHookPatch import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch -import app.revanced.patches.youtube.utils.request.buildRequestPatch -import app.revanced.patches.youtube.utils.request.hookBuildRequest import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow @@ -34,21 +33,10 @@ val playlistPatch = bytecodePatch( dismissPlayerHookPatch, playerTypeHookPatch, videoInformationPatch, - buildRequestPatch, + authHookPatch, ) execute { - // In Incognito mode, sending a request always seems to fail. - accountIdentityFingerprint.methodOrThrow().addInstructions( - 1, """ - sput-object p3, $EXTENSION_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String; - sput-boolean p4, $EXTENSION_CLASS_DESCRIPTOR->isIncognito:Z - """ - ) - - // Get the header to use the auth token. - hookBuildRequest("$EXTENSION_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V") - // Open the queue manager by pressing and holding the back button. getMainActivityMethod("onKeyLongPress") .addInstructionsWithLabels( From bbf863c630e00b781d85ac0718f1eea7f092a6c1 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:11:45 +0900 Subject: [PATCH 20/26] fix(YouTube - Description components): `Expand video descriptions` requires longer wait time --- .../extension/youtube/patches/player/PlayerPatch.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java index 6ee95843d..2c43c80b0 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java @@ -183,8 +183,8 @@ public class PlayerPatch { // The type of descriptionView can be either ViewGroup or TextView. (A/B tests) // If the type of descriptionView is TextView, longer delay is required. final long delayMillis = descriptionView instanceof TextView - ? 500 - : 100; + ? 750 + : 200; Utils.runOnMainThreadDelayed(() -> Utils.clickView(descriptionView), delayMillis); } From 0cc693961a50e1c5bc04b8b566a405dc4929440f Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:11:59 +0900 Subject: [PATCH 21/26] feat(Translations): Update translation --- .../youtube/translations/ar/strings.xml | 6 ++++ .../youtube/translations/de-rDE/strings.xml | 22 ++++++++++--- .../youtube/translations/el-rGR/strings.xml | 6 ++++ .../youtube/translations/es-rES/strings.xml | 6 ++++ .../youtube/translations/fr-rFR/strings.xml | 6 ++++ .../youtube/translations/it-rIT/strings.xml | 6 ++++ .../youtube/translations/ja-rJP/strings.xml | 26 +++++++++------ .../youtube/translations/ru-rRU/strings.xml | 10 ++++-- .../youtube/translations/uk-rUA/strings.xml | 6 ++++ .../youtube/translations/vi-rVN/strings.xml | 6 ++++ .../youtube/translations/zh-rTW/strings.xml | 32 +++++++++++-------- 11 files changed, 103 insertions(+), 29 deletions(-) diff --git a/patches/src/main/resources/youtube/translations/ar/strings.xml b/patches/src/main/resources/youtube/translations/ar/strings.xml index 3b31f2cb2..342a6eb37 100644 --- a/patches/src/main/resources/youtube/translations/ar/strings.xml +++ b/patches/src/main/resources/youtube/translations/ar/strings.xml @@ -22,11 +22,13 @@ إضافة إلى قائمة الانتظار إضافة إلى قائمة الانتظار وفتح قائمة الانتظار إضافة إلى قائمة الانتظار وتشغيل الفيديو + إضافة إلى قائمة الانتظار وإعادة تحميل الفيديو أداة التنزيل الخارجي فتح قائمة الانتظار قائمة الإنتظار إزالة من قائمة الانتظار إزالة من قائمة الانتظار وفتح قائمة الانتظار + إزالة من قائمة الانتظار وإعادة تحميل الفيديو إزالة قائمة الانتظار حفظ قائمة الانتظار "بدلاً من فتح برنامج تنزيل خارجي، افتح نافذة مدير قائمة الانتظار. @@ -2155,6 +2157,10 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص معلومات التعديل معلومات عن التعديلات المطبقة. + معلومات التطبيق + اسم التطبيق + إصدار التطبيق + تاريخ التعديل أخرى مخصص diff --git a/patches/src/main/resources/youtube/translations/de-rDE/strings.xml b/patches/src/main/resources/youtube/translations/de-rDE/strings.xml index 47ebb30cf..10e78f6a5 100644 --- a/patches/src/main/resources/youtube/translations/de-rDE/strings.xml +++ b/patches/src/main/resources/youtube/translations/de-rDE/strings.xml @@ -22,11 +22,13 @@ Bitte lade %2$s von der Webseite herunter." Zur Warteschlange hinzufügen Zur Warteschlange hinzufügen & öffnen Zur Warteschlange hinzufügen und Video abspielen + Zur Warteschlange hinzufügen und Video neu laden Externer Downloader Warteschlange öffnen Warteschlange Aus der Warteschlange entfernen Aus der Warteschlange entfernen und die Warteschlange öffnen + Aus der Warteschlange entfernen und Video neu laden Warteschlange löschen Warteschlange speichern "Öffnen Sie statt eines externen Downloaders den Warteschlangenmanager. @@ -113,7 +115,7 @@ Bitte verwenden Sie sie nur zu Debugging-Zwecken." "" Werbung - Endbild-Banner ausblenden + Abspann Store Banner ausblenden Store-Banner ist ausgeblendet. Store-Banner wird angezeigt. Vollbildwerbung verstecken @@ -791,7 +793,7 @@ Information: Autoplay kann in den YouTube-Einstellungen geändert werden: Einstellungen → Autoplay → Nächstes Video automatisch abspielen" - Empfohlene Video-Endbildschirm wird angezeigt. + Empfohlene Videos Endbildschirm wird angezeigt. Autotoplay Countdown überspringen Ist Autoplay aktiviert, wird das nächste Video sofort abgespielt. Wenn Autoplay aktiviert ist, wird das nächste Video nach dem Countdown abgespielt. @@ -1490,6 +1492,10 @@ Drücken und halten Sie die Schaltfläche Mehr, um den Dialog benutzerdefinierte Zeige geöffnetes Video-Menü Video-Menü wird angezeigt. Video-Menü öffnen ist ausgeblendet. + Geschwindigkeitsdialog + Menü „Geschwindigkeitsdialog anzeigen“ + Das Dialogmenü „Geschwindigkeit“ wird angezeigt. + Das „Geschwindigkeitsdialogmenü“ ist ausgeblendet. Status wiederholen Zeige Wiederholungsstatus Menü Das Statusmenü wird angezeigt. @@ -1594,6 +1600,7 @@ Keine Ränder oben und unten des Spielers." Video + Codec HDR-Video deaktivieren HDR-Video ist deaktiviert HDR-Video ist aktiviert @@ -1607,6 +1614,7 @@ Keine Ränder oben und unten des Spielers." Replace software AV1 codec Replaces the software AV1 codec with the VP9 codec. + Wiedergabegeschwindigkeit Standard Wiedergabegeschwindigkeit Remember playback speed changes Playback speed changes apply to all videos. @@ -1614,6 +1622,7 @@ Keine Ränder oben und unten des Spielers." Toast anzeigen Beim Ändern der Standard-Wiedergabegeschwindigkeit wird ein Toast angezeigt. Beim Ändern der Standard-Wiedergabegeschwindigkeit wird kein Toast angezeigt. + Standard Wiedergabegeschwindigkeit bei Shorts Benutzerdefinierte Wiedergabegeschwindigkeit aktivieren Benutzerdefinierte Wiedergabegeschwindigkeit ist aktiviert Benutzerdefinierte Wiedergabegeschwindigkeit ist deaktiviert @@ -1625,6 +1634,7 @@ Keine Ränder oben und unten des Spielers." Ungültige benutzerdefinierte Wiedergabegeschwindigkeiten. Auf Standardwerte zurücksetzen. Ungültige benutzerdefinierte Wiedergabegeschwindigkeiten. Auf Standardwerte zurücksetzen. + Videoqualität Standard Videoqualität im Mobilfunk Standard-Videoqualität im Wlan Qualitätseinstellungen merken @@ -1897,7 +1907,7 @@ Klicken Sie hier, um zu sehen, wie Sie einen API-Schlüssel ausgeben." sponsor.ajay.app Die Daten werden von der SponsorBlock API bereitgestellt. Tippen Sie hier, um mehr zu erfahren und Downloads für andere Plattformen zu sehen - PreferenceScreen: Sonstiges + Sonstiges URL-Weiterleitungen umgehen URL-Weiterleitungen werden umgangen. URL-Umleitungen werden nicht umgangen. @@ -1937,7 +1947,7 @@ Drücke Weiter und deaktiviere Akku-Optimierungen." Aktiviere den OPUS-Codec, wenn die Antwort des Players den OPUS-Codec enthält. Einstellungen importieren / exportieren - Einstellungen importieren / exportieren + Importieren / Exportieren der RVX Einstellungen. Als Datei importieren / exportieren Einstellungen exportieren @@ -2043,6 +2053,10 @@ Klicken Sie hier, um weitere Informationen zu sehen." Patch-Informationen Informationen über angewandte Patches + App Info + App Name + App Version + Patch Datum Andere Benutzerdefiniert diff --git a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml index d4e4616e2..37bd8d3fe 100644 --- a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml +++ b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml @@ -22,11 +22,13 @@ Προσθήκη στην ουρά Προσθήκη στην ουρά και άνοιγμα ουράς Προσθήκη στην ουρά και αναπαραγωγή βίντεο + Προσθήκη στην ουρά και επαναφόρτωση του βίντεο Εξωτερικό πρόγραμμα λήψης Άνοιγμα ουράς Ουρά Αφαίρεση από την ουρά Αφαίρεση από την ουρά και άνοιγμα ουράς + Αφαίρεση από την ουρά και επαναφόρτωση του βίντεο Κατάργηση ουράς Αποθήκευση ουράς "Αντί να ανοίξετε ένα εξωτερικό πρόγραμμα λήψης, ανοίξτε το παράθυρο διαχείρισης ουράς. @@ -2163,6 +2165,10 @@ Playlists Πληροφορίες τροποποίησης Πληροφορίες σχετικά με τις εφαρμοσμένες τροποποιήσεις. + Πληροφορίες εφαρμογής + Όνομα εφαρμογής + Έκδοση εφαρμογής + Ημερομηνία τροποποίησης Άλλα Προσαρμοσμένο diff --git a/patches/src/main/resources/youtube/translations/es-rES/strings.xml b/patches/src/main/resources/youtube/translations/es-rES/strings.xml index d6b4d4b79..89614e528 100644 --- a/patches/src/main/resources/youtube/translations/es-rES/strings.xml +++ b/patches/src/main/resources/youtube/translations/es-rES/strings.xml @@ -22,11 +22,13 @@ Por favor, descarga %2$s desde el sitio web." Añadir a la cola Añadir a la cola y abrir la cola Añadir a la cola y reproducir vídeo + Añadir a la cola y recargar vídeo Descargador externo Abrir cola Cola Eliminar de la cola Eliminar de la cola y abrir la cola + Eliminar de la cola y recargar vídeo Eliminar cola Guardar cola "En lugar de abrir un descargador externo, abre el diálogo del gestor de colas. @@ -2134,6 +2136,10 @@ Pulsa aquí para ver más información." Información de parches Información sobre los parches aplicados. + Información de la app + Nombre de la app + Versión de la app + Fecha de parcheado Otros Personalizado diff --git a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml index 8c00f96b1..e5f3c363b 100644 --- a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml +++ b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml @@ -22,11 +22,13 @@ Veuillez télécharger %2$s à partir du site web." Ajouter à la file d\'attente Ajouter à la file d\'attente et ouvrir la file d\'attente Ajouter à la file d\'attente et lire la vidéo + Ajouter à la file d\'attente et recharger la vidéo Téléchargeur externe Ouvrir la file d\'attente File d\'attente Retirer de la file d\'attente Retirer de la file d\'attente et ouvrir la file d\'attente + Supprimer de la file d\'attente et recharger la vidéo Retirer de la file d\'attente Enregistrer la file d’attente "Au lieu d’ouvrir un téléchargeur externe, ouvrez la boîte de dialogue du gestionnaire de file d’attente. @@ -2149,6 +2151,10 @@ Cliquez pour plus d'informations." Informations sur les patchs Informations sur les patchs appliqués. + Info sur l\'app + Nom de l\'application + Version de l\'appli + Date du patch Autres Personnalisé diff --git a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml index 3dfa6da46..6a4dab5e3 100644 --- a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml +++ b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml @@ -22,11 +22,13 @@ Si prega di scaricare %2$s dal sito web." Aggiungi alla coda Aggiungi alla coda e aprila Aggiungi alla coda e riproduci il video + Aggiungi alla coda e ricarica il video Downloader esterno Apri la coda Coda Rimuovi dalla coda Rimuovi dalla coda e aprila + Rimuovi dalla coda e ricarica il video Rimuovi la coda Salva la coda "Invece di aprire un downloader esterno, apri la finestra di dialogo del gestore delle code. @@ -2143,6 +2145,10 @@ Tocca per vedere maggiori informazioni." Informazioni patch Informazioni sulle patch applicate. + Informazioni sull\'app + Nome + Versione + Data e ora della patch Altri Personalizzata diff --git a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml index 5bc6cc711..f07abd14c 100644 --- a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml +++ b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml @@ -22,11 +22,13 @@ キューに追加 キューに追加してキューを開く キューに追加して動画を再生 + キューに追加して動画を再読み込み 外部ダウンローダー キューを開く キュー キューから削除 キューから削除してキューを開く + キューから削除して動画を再読み込み キューを削除 キューを保存 "外部ダウンローダーを開く代わりに、キューマネージャーダイアログを開きます。 @@ -410,7 +412,7 @@ DeArrow の詳細については、ここをタップしてください。"ショート スポーツ 登録チャンネル - トレンド + 急上昇 バーチャル リアリティ 後で見る クリップ @@ -838,9 +840,9 @@ DeArrow の詳細については、ここをタップしてください。"「共有」ボタンを非表示 「共有」ボタンを非表示にします。 「共有」ボタンを非表示にします。 - 「ショップ」ボタンを非表示 - 「ショップ」ボタンを非表示にします。 - 「ショップ」ボタンを非表示にします。 + 「ショッピング」ボタンを非表示 + 「ショッピング」ボタンを非表示にします。 + 「ショッピング」ボタンを非表示にします。 「Thanks」ボタンを非表示 「Thanks」ボタンを非表示にします。 「Thanks」ボタンを非表示にします。 @@ -1394,9 +1396,9 @@ DeArrow の詳細については、ここをタップしてください。"「チャンネル登録」ボタンを非表示 「チャンネル登録」ボタンを非表示にします。 「チャンネル登録」ボタンを非表示にします。 - 「トレンド」ボタンを非表示 - 「トレンド」ボタンを非表示にします。 - 「トレンド」ボタンを非表示にします。 + 「急上昇」ボタンを非表示 + 「急上昇」ボタンを非表示にします。 + 「急上昇」ボタンを非表示にします。 動画のタイトルを非表示 プレーヤー下部に表示される動画のタイトル名を非表示にします。 プレーヤー下部に表示される動画のタイトルを非表示にします。 @@ -1414,9 +1416,9 @@ DeArrow の詳細については、ここをタップしてください。"検索候補のボタンを非表示 検索候補のボタンを非表示にします。 検索候補のボタンを非表示にします。 - 「ショップ」ボタンを非表示 - 「ショップ」ボタンを非表示にします。 - 「ショップ」ボタンを非表示にします。 + 「ショッピング」ボタンを非表示 + 「ショッピング」ボタンを非表示にします。 + 「ショッピング」ボタンを非表示にします。 「Super Thanks」ボタンを非表示 「Super Thanks」ボタンを非表示にします。 「Super Thanks」ボタンを非表示にします。 @@ -2096,6 +2098,10 @@ iOS クライアント選択する場合は、これらの値が必要になる パッチ情報 適用されたパッチに関する情報です。 + アプリ情報 + アプリ名 + アプリバージョン + パッチ適用日時 その他 カスタム diff --git a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml index 9fb4f7fc8..f1ae49ca9 100644 --- a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml +++ b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml @@ -22,11 +22,13 @@ Добавить в очередь Добавить в очередь и открыть очередь Добавить в очередь и посмотреть + Добавить в очередь и обновить Внешний загрузчик Открыть очередь Очередь Удалить из очереди Удалить из очереди и открыть очередь + Удалить из очереди и обновить Удалить очередь Сохранить очередь "Вместо открытия внешнего загрузчика открывается диалог управления очередью. @@ -1248,7 +1250,7 @@ Shorts "Коснитесь, чтобы создать список просмотра всех видео из канала. Коснитесь и удерживайте, для отмены. -Инфо: +Информация: • Может не работать в прямых трансляциях." Сортировка создаваемого списка Все (сорт. по времени, по возрастанию) @@ -2104,7 +2106,7 @@ Shorts Использовать клиент iOS "Клиент iOS добавлен к доступным клиентам. -ВНИМАНИЕ: Клиент iOS устаревший. Любые проблемы, возникающие при его использовании, на свой страх и риск." +ПРЕДУПРЕЖДЕНИЕ: Клиент iOS устаревший. Любые проблемы, возникающие при его использовании, на свой страх и риск." Клиент iOS не добавлен к доступным клиентам. "При запросе конечных точек YouTube API с помощью iOS требуются токены Auth, выданные устройством iOS, и токены PoToken, выданные iOSGuard. @@ -2165,6 +2167,10 @@ Shorts Информация о патчах Информация о примененных патчах. + О приложении + Имя приложения + Версия приложения + Дата применения патчей Другие Пользовательский diff --git a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml index 28669b5eb..9547b2c5c 100644 --- a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml +++ b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml @@ -22,11 +22,13 @@ Додати до черги Додати до черги й відкрити чергу Додати до черги й відтворити відео + Додати до черги та перевантажити відео Зовнішній завантажувач Відкрити чергу Черга Видалити з черги Вилучити з черги й відкрити чергу + Видалити з черги та перевантажити відео Вилучити чергу Зберегти чергу "Замість відкривання зовнішнього завантажувача відкриває діалог керування чергою. @@ -2148,6 +2150,10 @@ AVC має максимальну роздільну здатність 1080p, Інформація про патчі Інформація про застосовані патчі. + Інформація про додаток + Назва додатка + Версія додатка + Дата пропатчування Інше Користувацька diff --git a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml index c47d1c600..e5bdc56fc 100644 --- a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml +++ b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml @@ -22,11 +22,13 @@ Thêm vào hàng chờ Thêm vào hàng chờ và xem hàng chờ Thêm vào hàng chờ và phát video + Thêm vào hàng chờ và tải lại video Trình tải xuống bên ngoài Xem hàng chờ Hàng chờ Xóa khỏi hàng chờ Xóa khỏi hàng chờ và xem hàng chờ + Xoá khỏi hàng chờ và tải lại video Xoá hàng chờ Lưu hàng chờ "Thay vì mở trình tải xuống bên ngoài, khi tương tác sẽ mở hộp thoại quản lý hàng chờ. @@ -2151,6 +2153,10 @@ Nhấp vào đây để biết thêm chi tiết." Thông tin bản vá Thông tin về các bản vá được áp dụng. + Thông tin ứng dụng + Tên ứng dụng + Phiên bản ứng dụng + Ngày vá Khác Tùy chỉnh diff --git a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml index 6b8941b29..d9d10db66 100644 --- a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml +++ b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml @@ -22,11 +22,13 @@ 加入佇列清單 加到佇列並打開佇列 加入佇列並播放影片 + 加入佇列並重新載入影片 外部下載器 開啟佇列 佇列 從佇列中移除 從佇列中移除並打開佇列 + 從佇列移除並重新載入影片 移除佇列 儲存佇列 "不要開啟外部下載器,而是開啟佇列管理器對話方塊。 @@ -506,15 +508,15 @@ 平板 平板電腦 (最少 600 dp) 車用 - "改變包括: + "變更內容包括: 平板佈局 -• 社群貼文被隱藏。 +• 隱藏社群貼文。 車用佈局 -• 常規播放器中的短影音開啟。 -• 資訊流依主題和頻道進行組織。 -• 關閉「偽裝串流數據」時無法開啟影片說明。" +• 一般播放器中的短影音開啟。 +• 動態消息按主題和頻道分類。 +• 關閉「偽裝串流數據」時無法開啟影片描述。" 禁用佈局更新 伺服器不會更新佈局。 佈局將由伺服器更新。 @@ -1362,9 +1364,9 @@ 手動展開影片描述 短影音 - 停用 短影音 後台播放 - 短影音 後台播放已停用。 - 短影音 後台播放已啟用。 + 停用短影音背景播放 + 短影音背景播放已停用。 + 短影音背景播放已啟用。 停用恢復短影音播放器 短影音播放器在應用程式啟動時不會恢復播放。 短影音播放器在應用程式啟動時會恢復播放。 @@ -1396,15 +1398,15 @@ 在觀看歷史中隱藏 在觀看歷史中顯示 - 變更 短影音背景重複狀態 - 更改短影音重複播放狀態 + 變更短影音背景重複狀態 + 變更短影音重複播放狀態 自動播放 預設 暫停 重複播放 - 在一般播放器中開啟 Shorts - 在一般播放器中開啟 Shorts。 - 不要在普通播放器中開啟 Shorts。 + 在一般播放器中開啟短影音 + 在一般播放器中開啟短影音。 + 不要在一般播放器中開啟短影音。 短影音播放器 隱藏或顯示短影音播放器中的組件。 @@ -2139,6 +2141,10 @@ AVC 的最大解析度為 1080p,Opus 音訊編解碼器不可用,影片播 補丁訊息 已應用補丁的訊息 + 應用程式資訊 + 應用程式名稱 + 應用程式版本 + 修補日期 其他 自訂 From 2b523802942ab947f63702b3e1ac2bdf60d98dfb Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:12:45 +0900 Subject: [PATCH 22/26] bump 5.6.2-dev.2 --- gradle.properties | 2 +- patches.json | 22 ++++++++++++++++++++-- patches/api/patches.api | 8 +++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0b13e5b33..6dbe27e09 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official kotlin.jvm.target.validation.mode = IGNORE -version = 5.6.2-dev.1 +version = 5.6.2-dev.2 diff --git a/patches.json b/patches.json index 72c3927ef..09e8f3fde 100644 --- a/patches.json +++ b/patches.json @@ -1024,8 +1024,7 @@ "description": "Adds an option to disable the popup that appears when taking a screenshot.", "use": true, "dependencies": [ - "Settings for Reddit", - "ResourcePatch" + "Settings for Reddit" ], "compatiblePackages": { "com.reddit.frontpage": [ @@ -1310,6 +1309,15 @@ "Clone": "com.rvx.android.apps.youtube.music", "Default": "app.rvx.android.apps.youtube.music" } + }, + { + "key": "patchAllManifest", + "title": "Patch all manifest components", + "description": "Patch all permissions, intents and content provider authorities supported by GmsCore.", + "required": true, + "type": "kotlin.Boolean", + "default": true, + "values": null } ] }, @@ -1374,6 +1382,15 @@ "Clone": "com.rvx.android.apps.youtube.music", "Default": "app.rvx.android.apps.youtube.music" } + }, + { + "key": "patchAllManifest", + "title": "Patch all manifest components", + "description": "Patch all permissions, intents and content provider authorities supported by GmsCore.", + "required": true, + "type": "kotlin.Boolean", + "default": true, + "values": null } ] }, @@ -3097,6 +3114,7 @@ "BytecodePatch", "BytecodePatch", "BytecodePatch", + "BytecodePatch", "ResourcePatch" ], "compatiblePackages": { diff --git a/patches/api/patches.api b/patches/api/patches.api index f4d107275..a314dc173 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -414,7 +414,7 @@ public final class app/revanced/patches/reddit/layout/screenshotpopup/Screenshot } public final class app/revanced/patches/reddit/layout/subredditdialog/FingerprintsKt { - public static final fun indexOfGetOverInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I + public static final fun indexOfHasBeenVisitedInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I public static final fun indexOfSetBackgroundTintListInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I public static final fun listOfIsLoggedInInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/util/List; } @@ -459,9 +459,7 @@ public final class app/revanced/patches/reddit/utils/extension/SharedExtensionPa } public final class app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatchKt { - public static final fun getActionShare ()J public static final fun getNsfwDialogTitle ()J - public static final fun getScreenShotShareBanner ()J } public final class app/revanced/patches/reddit/utils/settings/SettingsPatchKt { @@ -897,6 +895,10 @@ public final class app/revanced/patches/youtube/utils/FingerprintsKt { public static final fun indexOfSpannedCharSequenceInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I } +public final class app/revanced/patches/youtube/utils/auth/AuthHookPatchKt { + public static final fun getAuthHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/youtube/utils/bottomsheet/BottomSheetHookPatchKt { public static final fun getBottomSheetHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } From 7168e49121ca4ba2a995f43cf3e322f726575e6d Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:18:02 +0900 Subject: [PATCH 23/26] fix(Reddit - Disable screenshot popup): No exception is thrown if no matching pattern is found --- .../reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt index 4c4653142..096fa09be 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/screenshotpopup/ScreenshotPopupPatch.kt @@ -2,6 +2,7 @@ package app.revanced.patches.reddit.layout.screenshotpopup import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH @@ -57,6 +58,8 @@ val screenshotPopupPatch = bytecodePatch( indexOfSetValueInstruction(this) >= 0 } + var hookCount = 0 + classes.forEach { classDef -> classDef.methods.forEach { method -> if (method.isScreenShotMethod()) { @@ -75,11 +78,16 @@ val screenshotPopupPatch = bytecodePatch( move-result-object v$booleanRegister """ ) + hookCount++ } } } } + if (hookCount == 0) { + throw PatchException("Failed to find hook method") + } + updatePatchStatus( "enableScreenshotPopup", DISABLE_SCREENSHOT_POPUP From 8f23d76f37caa34898a0e325e62a4962f64b4cb7 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:19:36 +0900 Subject: [PATCH 24/26] feat(Reddit): Change the latest supported version from `2025.12.0` to `2025.12.1` --- .../revanced/patches/reddit/utils/compatibility/Constants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/utils/compatibility/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/compatibility/Constants.kt index acb655efa..2feb4a8c5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/utils/compatibility/Constants.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/utils/compatibility/Constants.kt @@ -11,7 +11,7 @@ internal object Constants { setOf( "2024.17.0", // This is the last version that can be patched without anti-split. "2025.05.1", // This was the latest version supported by the previous RVX patch. - "2025.12.0", // This is the latest version supported by the RVX patch. + "2025.12.1", // This is the latest version supported by the RVX patch. ) ) } \ No newline at end of file From db10e410d35f1a0a2887fb7b7bb52a5cddcf244d Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:20:10 +0900 Subject: [PATCH 25/26] feat(Translations): Update translation --- .../music/translations/ko-rKR/strings.xml | 2 +- .../youtube/translations/ar/strings.xml | 42 ++++++++--------- .../youtube/translations/ko-rKR/strings.xml | 2 +- .../youtube/translations/pt-rBR/strings.xml | 46 +++++++++++++++++++ .../youtube/translations/uk-rUA/strings.xml | 2 +- 5 files changed, 69 insertions(+), 25 deletions(-) diff --git a/patches/src/main/resources/music/translations/ko-rKR/strings.xml b/patches/src/main/resources/music/translations/ko-rKR/strings.xml index 7f7bbfbef..992bef881 100644 --- a/patches/src/main/resources/music/translations/ko-rKR/strings.xml +++ b/patches/src/main/resources/music/translations/ko-rKR/strings.xml @@ -183,7 +183,7 @@ 이전 보관함 선반으로 복원 이전 보관함 탭으로 복원합니다. (실험 기능) 시청 경고 다이얼로그 제거 - "다음 콘텐츠를 재생하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 콘텐츠\n• 혐오감을 주는 콘텐츠\n• 자살 또는 자해와 관련된 콘텐츠 ...\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다." + "다음 콘텐츠를 재생하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 콘텐츠\n• 자살 또는 자해와 관련된 콘텐츠, etc.\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다." 앱 버전 변경 "이전 앱 버전으로 변경합니다. diff --git a/patches/src/main/resources/youtube/translations/ar/strings.xml b/patches/src/main/resources/youtube/translations/ar/strings.xml index 342a6eb37..25f691922 100644 --- a/patches/src/main/resources/youtube/translations/ar/strings.xml +++ b/patches/src/main/resources/youtube/translations/ar/strings.xml @@ -205,18 +205,18 @@ تم إخفاء الترقية لـ YouTube Premium. يتم عرض الترقية لـ YouTube Premium. - مُصغَّرات فيديو بديلة + مصغرات فيديو بديلة علامة تبويب الصفحة الرئيسية قوائم تشغيل المشغل، التوصيات نتائج البحث علامة تبويب الاشتراكات علامة التبويب أنت - المصّغرات الأصلية - DeArrow & المصّغرات الأصلية + المصغرات الأصلية + DeArrow & المصغرات الأصلية DeArrow & اللقطات الثابتة اللقطات الثابتة DeArrow - "يوفر DeArrow مُصغَّرات فيديو تم جمعها من الجمهور لفيديوهات YouTube. غالبًا ما تكون مُصغَّرات الفيديو هذه ذات صلة أكثر من تلك التي يقدمها موقع YouTube. + "يوفر DeArrow مصغرات فيديو تم جمعها من الجمهور لفيديوهات YouTube. غالبًا ما تكون مصغرات الفيديو هذه ذات صلة أكثر من تلك التي يقدمها موقع YouTube. اذا تم تمكين هذا الخيار، سيتم إرسال عناوين URL للفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا لم يكن الفيديو يحتوي على مُصغَّرات لـ DeArrow، فسيتم عرض اللقطات الأصلية أو الثابتة. @@ -230,7 +230,7 @@ لقطات الفيديو الثابتة يتم التقاط اللقطات الثابتة من بداية أو منتصف أو نهاية كل فيديو. هذه الصور مدمجة في YouTube ولا يتم استخدام أي واجهة برمجة تطبيقات خارجية. استخدام اللقطات الثابتة السريعة - استخدام اللقطات متوسطة الجودة. سيتم تحميل المُصغَّرات بشكل أسرع، ولكن البث المباشر أو المقاطع التي لم يتم إصدارها أو القديمة جدًا قد تعرض مُصغَّرات فارغة. + استخدام اللقطات متوسطة الجودة. سيتم تحميل المصغرات بشكل أسرع، ولكن البث المباشر أو المقاطع التي لم يتم إصدارها أو القديمة جدًا قد تعرض مصغرات فارغة. استخدام لقطات الفيديو الثابتة بجودة عالية. وقت الفيديو لأخذ اللقطات الثابتة منه بداية الفيديو @@ -248,9 +248,9 @@ إخفاء بطاقات الألبوم تم إخفاء بطاقات الألبوم. يتم عرض بطاقات الألبوم. - إخفاء زر التَرْجَمَة - تم إخفاء زر التَرْجَمَة. - يتم عرض زر التَرْجَمَة. + إخفاء زر الترجمة + تم إخفاء زر الترجمة. + يتم عرض زر الترجمة. إخفاء الرفوف الدوارة "تم إخفاء الرفوف الدوارة، مثل: • أخبار عاجلة @@ -983,12 +983,12 @@ إخفاء قائمة المقطع الصوتي تم إخفاء قائمة المقطع الصوتي. يتم عرض قائمة المقطع الصوتي. - إخفاء قائمة التَرْجَمَة - تم إخفاء قائمة التَرْجَمَة. - يتم عرض قائمة التَرْجَمَة. - إخفاء تذييل قائمة التَرْجَمَة - تم إخفاء تذييل قائمة التَرْجَمَة. - يتم عرض تذييل قائمة التَرْجَمَة. + إخفاء قائمة الترجمة + تم إخفاء قائمة الترجمة. + يتم عرض قائمة الترجمة. + إخفاء تذييل قائمة الترجمة + تم إخفاء تذييل قائمة الترجمة. + يتم عرض تذييل قائمة الترجمة. إخفاء قائمة قفل الشاشة تم إخفاء قائمة قفل الشاشة. يتم عرض قائمة قفل الشاشة. @@ -1193,9 +1193,9 @@ إخفاء زر التشغيل التلقائي تم إخفاء زر التشغيل التلقائي. يتم عرض زر التشغيل التلقائي. - إخفاء زر التَرْجَمَة - تم إخفاء زر التَرْجَمَة. - يتم عرض زر التَرْجَمَة. + إخفاء زر الترجمة + تم إخفاء زر الترجمة. + يتم عرض زر الترجمة. إخفاء زر البث تم إخفاء زر البث. يتم عرض زر البث. @@ -1639,9 +1639,7 @@ النسبة المئوية لمساحة الشاشة القابلة للتمرير السريع.\n\nملاحظة: سيؤدي هذا أيضًا إلى تغيير حجم مساحة الشاشة لإيماءة النقر المزدوج للتقديم أو التأخير. لا يمكن أن يزيد حجم المنطقة القابلة للتمرير السريع عن 50. مهلة واجهة إيماءة التمرير - مقدار الوقت الذي تظهر فيه واجهة التمرير بعد التغيير (بجزء الثانية). - -الافتراضي:500 + أجزاء الثانية التي تكون فيها الواجهة مرئية. حساسية تمرير مستوى السطوع تكوين الحد الأدنى للمسافة لتمرير السطوع بين 1 و1000 (%).\nكلما كانت المسافة الدنيا أقصر، كلما تغيرت مستويات السطوع بشكل أسرع. يجب أن تكون حساسية تمرير مستوى السطوع بين 1-1000 (%). @@ -1819,7 +1817,7 @@ SponsorBlock تمكين SponsorBlock - SponsorBlock مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المزعجة من فيديوهات YouTube. + مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المزعجة من فيديوهات YouTube. المظهر عرض زر التصويت @@ -1907,7 +1905,7 @@ يتم عرض زر إنشاء مقطع جديد. لا يتم عرض زر إنشاء مقطع جديد. تعديل تقديم او تأخير المقطع الجديد - أجزاء الثانية في الوقت الذي تتحرك فيها أزرار ضبط الوقت عند إنشاء مقاطع جديدة. + أجزاء الثانية في الوقت الذي تتحرك فيه أزرار ضبط الوقت عند إنشاء مقاطع جديدة. يجب أن تكون القيمة رقمًا موجبًا. عرض الإرشادات الإرشادات تحتوي على نصائح حول تقديم المقاطع. diff --git a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml index dda719bbe..f65ac6c2e 100644 --- a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml +++ b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml @@ -500,7 +500,7 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요." 동영상들 사이에서 회색 구분선이 숨겨집니다. 동영상들 사이에서 회색 구분선이 표시됩니다. 시청 경고 다이얼로그 제거하기 - "다음 동영상을 시청하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 동영상\n• 혐오감을 주는 동영상\n• 자살 또는 자해와 관련된 동영상, etc.\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다." + "다음 동영상을 시청하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 동영상\n• 자살 또는 자해와 관련된 동영상, etc.\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다." 라이브 링 누르기 동작 변경하기 "라이브 링이 표시된 채널 아이콘을 누르면 채널 프로필으로 연결됩니다. diff --git a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml index 4935df26b..c3bf01685 100644 --- a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml +++ b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml @@ -22,11 +22,13 @@ Por favor, baixe %2$s do site." Adicionar à fila Adicionar à fila e abrir a fila Adicionar à fila e reproduzir o vídeo + Adicionar à fila e recarregar o vídeo Download externo Abrir fila Fila Remover da lista Remover da fila e abrir a fila + Remover da fila e recarregar vídeo Remover fila Salvar fila "Em vez de abrir um aplicativo de download externo, abra a caixa de diálogo do gerenciador de filas. @@ -53,14 +55,50 @@ Use-o apenas para fins de depuração." Fila salva com sucesso em \'%s\'. Idioma do RVX Idioma do app + "Catalão +Català" + "Checo +Čeština" + "Alemão +Deutsch" + "Grego +Ελληνικά" + "Inglês +English" + "Espanhol +Español" "Francês Français" + "Hebraico +עברי" + "Italiano +Italiano" + "Japonês +日本語" + "Coreano +한국어" + "Holandês +Nederlands" + "Polonês +Polski" "Português Português" "Romeno Română" "Russo Русский" + "Sérvio +Српски" + "Sueco +Svenska" + "Tailandês +ไทย" + "Turco +Türkçe" + "Ucraniano +Українська" + "Chinês +中文" Anúncios Ocultar banner da loja na tela final @@ -1710,6 +1748,7 @@ Clique para ver como emitir uma chave de API." Exibir botão de pular Exibir na barra de progresso Desativar + Opacidade: Cor: Cor alterada. Redefinir cor. @@ -1894,6 +1933,8 @@ Toque no botão continuar e desative as otimizações da bateria." Android VR "Android VR (Sem autenticação)" + "iOS +(Obsoleto)" "iOS TV (é necessário logar)" Efeitos colaterais da falsificação @@ -1926,6 +1967,7 @@ O AVC tem uma resolução máxima de 1080p, o codec de áudio Opus não está di O cliente usado para buscar dados de streaming é mostrado em Estatísticas para nerds. O cliente usado para buscar dados de streaming está oculto em Estatísticas para nerds. Idioma padrão de áudio do VR + Você não pode estar logado. PoToken / VisitorData PoToken a ser utilizado @@ -1958,6 +2000,10 @@ Clique para ver mais informações." Informações do patch Informações sobre as modificações aplicadas. + Informações do aplicativo + Nome do aplicativo + Versão do aplicativo + Data da atualização Outros Personalizado diff --git a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml index 9547b2c5c..a46c46feb 100644 --- a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml +++ b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml @@ -2116,7 +2116,7 @@ AVC має максимальну роздільну здатність 1080p, Клієнт, що використовується для отримання даних трансляції показується у Статистика для сисадмінів. Клієнт, що використовується для отримання даних трансляції приховано у Статистика для сисадмінів. Мова звукової доріжки для VR - Не вдалося отримати якісь трансляції клієнта. + Не вдалося отримати які-небудь трансляції клієнта. Не можливо авторизуватися. PoToken / VisitorData From 15d0fafcf8c5e47a673a8c92aaf8aea1d710834c Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:20:34 +0900 Subject: [PATCH 26/26] bump 5.6.2 --- README.md | 28 ++++++++++++++-------------- gradle.properties | 2 +- patches.json | 26 +++++++++++++------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index cb7f710c0..6c0eb44df 100644 --- a/README.md +++ b/README.md @@ -134,19 +134,19 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm | 💊 Patch | 📜 Description | 🏹 Target Version | |:--------:|:--------------:|:-----------------:| -| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 | -| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 | -| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.0 | -| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.0 | -| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.0 | -| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.0 | -| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.0 | -| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.0 | -| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.0 | -| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.0 | -| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.0 | -| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.0 | -| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.0 | +| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 | +| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 | +| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.1 | +| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.1 | +| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.1 | +| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.1 | +| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.1 | +| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.1 | +| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.1 | +| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.1 | +| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.1 | +| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.1 | +| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.1 | @@ -200,7 +200,7 @@ Example: "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] diff --git a/gradle.properties b/gradle.properties index 6dbe27e09..f6daeb24a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official kotlin.jvm.target.validation.mode = IGNORE -version = 5.6.2-dev.2 +version = 5.6.2 diff --git a/patches.json b/patches.json index 09e8f3fde..7a1f5fe8e 100644 --- a/patches.json +++ b/patches.json @@ -188,7 +188,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [ @@ -485,7 +485,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [ @@ -1030,7 +1030,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -1405,7 +1405,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -1554,7 +1554,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -1706,7 +1706,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -1807,7 +1807,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2005,7 +2005,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2022,7 +2022,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2178,7 +2178,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2236,7 +2236,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2422,7 +2422,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [] @@ -2482,7 +2482,7 @@ "com.reddit.frontpage": [ "2024.17.0", "2025.05.1", - "2025.12.0" + "2025.12.1" ] }, "options": [