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