From 8ba9a19ade24c5fe9bd6d4e49772b7663522780e Mon Sep 17 00:00:00 2001
From: brosssh <44944126+brosssh@users.noreply.github.com>
Date: Thu, 18 Sep 2025 08:13:46 +0200
Subject: [PATCH] feat(Instagram): Add `Limit feed to followed profiles` patch
(#5908)
---
extensions/instagram/build.gradle.kts | 3 +
.../instagram/src/main/AndroidManifest.xml | 1 +
.../feed/LimitFeedToFollowedProfiles.java | 18 ++++++
patches/api/patches.api | 8 +++
.../patches/instagram/feed/Fingerprints.kt | 20 ++++++
.../feed/LimitFeedToFollowedProfiles.kt | 63 +++++++++++++++++++
.../hide/navigation/HideNavigationButtons.kt | 2 +-
.../misc/extension/SharedExtensionPatch.kt | 9 +++
.../extension/hooks/ApplicationInitHook.kt | 9 +++
9 files changed, 132 insertions(+), 1 deletion(-)
create mode 100644 extensions/instagram/build.gradle.kts
create mode 100644 extensions/instagram/src/main/AndroidManifest.xml
create mode 100644 extensions/instagram/src/main/java/app/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles.java
create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt
create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt
create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/SharedExtensionPatch.kt
create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt
diff --git a/extensions/instagram/build.gradle.kts b/extensions/instagram/build.gradle.kts
new file mode 100644
index 000000000..8cf6305c1
--- /dev/null
+++ b/extensions/instagram/build.gradle.kts
@@ -0,0 +1,3 @@
+dependencies {
+ compileOnly(project(":extensions:shared:library"))
+}
diff --git a/extensions/instagram/src/main/AndroidManifest.xml b/extensions/instagram/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9b65eb06c
--- /dev/null
+++ b/extensions/instagram/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/extensions/instagram/src/main/java/app/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles.java b/extensions/instagram/src/main/java/app/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles.java
new file mode 100644
index 000000000..2367e738a
--- /dev/null
+++ b/extensions/instagram/src/main/java/app/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles.java
@@ -0,0 +1,18 @@
+package app.revanced.extension.instagram.feed;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings("unused")
+public class LimitFeedToFollowedProfiles {
+
+ /**
+ * Injection point.
+ */
+ public static Map setFollowingHeader(Map requestHeaderMap) {
+ // Create new map as original is unmodifiable.
+ Map patchedRequestHeaderMap = new HashMap<>(requestHeaderMap);
+ patchedRequestHeaderMap.put("pagination_source", "following");
+ return patchedRequestHeaderMap;
+ }
+}
diff --git a/patches/api/patches.api b/patches/api/patches.api
index 00b58e88e..f54cb57e8 100644
--- a/patches/api/patches.api
+++ b/patches/api/patches.api
@@ -264,6 +264,10 @@ public final class app/revanced/patches/instagram/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
+public final class app/revanced/patches/instagram/feed/LimitFeedToFollowedProfilesKt {
+ public static final fun getLimitFeedToFollowedProfiles ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt {
public static final fun getHideExportFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -276,6 +280,10 @@ public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt {
public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
+public final class app/revanced/patches/instagram/misc/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt {
public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt
new file mode 100644
index 000000000..cf8c61118
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.instagram.feed
+
+import app.revanced.patcher.fingerprint
+import app.revanced.patcher.patch.BytecodePatchContext
+
+internal val mainFeedRequestClassFingerprint = fingerprint {
+ strings("Request{mReason=", ", mInstanceNumber=")
+}
+
+context(BytecodePatchContext)
+internal val initMainFeedRequestFingerprint get() = fingerprint {
+ custom { method, classDef ->
+ method.name == "" &&
+ classDef == mainFeedRequestClassFingerprint.classDef
+ }
+}
+
+internal val mainFeedHeaderMapFinderFingerprint = fingerprint {
+ strings("pagination_source", "FEED_REQUEST_SENT")
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt
new file mode 100644
index 000000000..3ded4c91a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt
@@ -0,0 +1,63 @@
+package app.revanced.patches.instagram.feed
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch
+import app.revanced.util.getReference
+import app.revanced.util.indexOfFirstInstructionOrThrow
+import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
+import com.android.tools.smali.dexlib2.iface.reference.FieldReference
+
+internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles;"
+
+@Suppress("unused")
+val limitFeedToFollowedProfiles = bytecodePatch(
+ name = "Limit feed to followed profiles",
+ description = "Filters the home feed to display only content from profiles you follow.",
+) {
+ compatibleWith("com.instagram.android")
+
+ dependsOn(sharedExtensionPatch)
+
+ execute {
+ /**
+ * Since the header field is obfuscated and there is no easy way to identify it among all the class fields,
+ * an additional method is fingerprinted.
+ * This method uses the map, so we can get the field name of the map field using this.
+ */
+ val mainFeedRequestHeaderFieldName: String
+
+ with(mainFeedHeaderMapFinderFingerprint.method) {
+ mainFeedRequestHeaderFieldName = indexOfFirstInstructionOrThrow {
+ getReference().let { ref ->
+ ref?.type == "Ljava/util/Map;" &&
+ ref.definingClass == mainFeedRequestClassFingerprint.classDef.toString()
+
+ }
+ }.let { instructionIndex ->
+ getInstruction(instructionIndex).getReference()!!.name
+ }
+ }
+
+ initMainFeedRequestFingerprint.method.apply {
+ // Finds the instruction where the map is being initialized in the constructor
+ val getHeaderIndex = indexOfFirstInstructionOrThrow {
+ getReference().let {
+ it?.name == mainFeedRequestHeaderFieldName
+ }
+ }
+
+ val paramHeaderRegister = getInstruction(getHeaderIndex).registerA
+
+ // Replace the `pagination_source` header value with `following` in the feed/timeline request.
+ addInstructions(
+ getHeaderIndex,
+ """
+ invoke-static { v$paramHeaderRegister }, $EXTENSION_CLASS_DESCRIPTOR->setFollowingHeader(Ljava/util/Map;)Ljava/util/Map;
+ move-result-object v$paramHeaderRegister
+ """
+ )
+ }
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt
index ee68c4fe1..cac9c35c3 100644
--- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt
@@ -49,7 +49,7 @@ val hideNavigationButtonsPatch = bytecodePatch(
val freeRegister = findFreeRegister(insertIndex, loopIndexRegister)
val instruction = getInstruction(endIndex - 1)
- var instructions = buildString {
+ val instructions = buildString {
if (hideCreate!!) {
appendLine(
"""
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/SharedExtensionPatch.kt
new file mode 100644
index 000000000..351b641a4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/SharedExtensionPatch.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.instagram.misc.extension
+
+import app.revanced.patches.instagram.misc.extension.hooks.applicationInitHook
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(
+ "instagram",
+ applicationInitHook,
+)
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt
new file mode 100644
index 000000000..eca0a885f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.instagram.misc.extension.hooks
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+
+internal val applicationInitHook = extensionHook {
+ custom { method, classDef ->
+ method.name == "onCreate" && classDef.endsWith("/InstagramAppShell;")
+ }
+}