instagram is where a lot of my conversations happen. opening the app can also mean landing in home or reels, which can easily lead to doomscrolling. i wanted a version that made checking messages feel like the main thing
picogram is a ReVanced patch for Instagram 453.0.0.57.79 (com.instagram.android). it keeps dms and profile in the bottom bar, opens to dms, blocks a small set of feed requests, and stops swipe navigation and pull-to-refresh in reels. the idea is pretty simple: make it easier to get to messages, then leave the rest of the app out of the way
what the patch changes
instagram represents its tabs as enum values. the patch replaces the method that builds the tab list with one that returns just dms and profile, then changes the starting destination to dms
the feed filter runs in TigonServiceLayer.startRequestInternal. it reads the request URI and passes it to a small helper bundled with the patch. the helper cancels requests whose paths contain these fragments:
/feed/timeline//feed/reels_tray/clips/home//clips/discover/clips/get_blend_medias//discover/topical_explore
that’s a short list of routes i wanted to keep out of the main flow. it isn’t a general Instagram network filter; other content reachable from profile, links or messages can still show up
for reels, the patch disables touch input on the relevant ViewPager2 and makes ClipsSwipeRefreshLayout.onInterceptTouchEvent() return false. a reel someone sends in a DM can still play, but swiping won’t take you to the next one. i checked that its comments opened too
a look at the patch
here’s the tab-list replacement. it creates a new list and adds only the dms and profile entries:
method("LX/00WQ;", "A04").addInstructions(0, """
new-instance v0, Ljava/util/ArrayList;
invoke-dms {v0}, Ljava/util/ArrayList;-><init>()V
sget-object v1, LX/03ai;->A0A:LX/03ai;
invoke-virtual {v0, v1}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
sget-object v1, LX/03ai;->A0F:LX/03ai;
invoke-virtual {v0, v1}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
return-object v0
""")
the request hook finds where the URI is read, then inserts a call to the helper right after it:
val register = (instructions[index] as TwoRegisterInstruction).registerA
request.addInstructions(
index + 1,
"invoke-static/range {v$register .. v$register}, " +
"Lcom/inboxonly/FeedBlock;->check(Ljava/net/URI;)V"
)
the helper checks the path and throws an IOException for the routes in the list:
public static void check(URI uri) throws IOException {
if (uri == null || uri.getPath() == null) return;
String path = uri.getPath();
if (path.contains("/feed/timeline/")
|| path.contains("/feed/reels_tray")
|| path.contains("/clips/home/")
|| path.contains("/clips/discover")
|| path.contains("/clips/get_blend_medias/")
|| path.contains("/discover/topical_explore")) {
throw new IOException("Blocked feed request");
}
}
the reel pager change turns off touch input on ViewPager2; the refresh container stops intercepting the gesture:
const/4 v1, 0x0
invoke-virtual {v0, v1}, Landroidx/viewpager2/widget/ViewPager2;->setUserInputEnabled(Z)V
const/4 v0, 0x0
return v0
these are the interesting bits, but they aren’t the whole patch. the bundle also has the class and method lookups that help find the right spots in the target apk
building it
Instagram’s play download came as split apk files, so i used AntiSplit-M to merge them into one apk for patching
the ReVanced patch is written in Kotlin with the bytecode patch api. it targets methods and classes from this release, including obfuscated navigation names like LX/00WQ; and LX/03ai;. i compile the helper to DEX and bundle it as extensions/inbox.rve in the .rvp file
those exact class and method names are also the main limitation: Picogram targets one Instagram build. if Instagram moves the code around, the patch fingerprints and injection points need another look before it can support a newer version
testing it on a phone
i applied the bundle to the matching apk and tried it on a Pixel 7. the app opened to dms, the bottom bar showed dms and profile, and a reel opened from a DM kept playing after a swipe instead of advancing
the project was a useful way to learn how bytecode patches fit together, from finding methods in the apk to building a ReVanced bundle and trying it on a device. and for my own use, the result is closer to what i wanted: open the app, check my messages, get on with the day