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] 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") } }