Skip to content

feat(Instagram): Save and view received Instants - #1538

Open
vishnuprasad0 wants to merge 8 commits into
crimera:devfrom
vishnuprasad0:feat/instants-download
Open

feat(Instagram): Save and view received Instants#1538
vishnuprasad0 wants to merge 8 commits into
crimera:devfrom
vishnuprasad0:feat/instants-download

Conversation

@vishnuprasad0

@vishnuprasad0 vishnuprasad0 commented Jul 18, 2026

Copy link
Copy Markdown

Adds a "Save Instants" patch that passively captures view-once Instants as you view them. Each instant's CDN links are recorded into a local vault (PikoInstantsDb), and a new "Saved Instants" screen (InstantsVaultActivity) lets you browse them grouped by sender, tap to download/open/copy, or long-press to delete.
The obfuscated VM → state → item → Media chain is resolved at patch time from the target dex using a -100 sentinel anchor — no hardcoded obfuscated names.
We added the cdn url at capture time, to be honst idk how many days cdn links survive . open for reviews and suggestions..

Closes #1202, #1139, #1454

}

private static Object declaredField(Object obj, String name) throws Exception {
Field f = obj.getClass().getDeclaredField(name);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use Entity class for this I suppose

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ys Capture now runs through the existing MediaData entity for the urls/username/type

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are still used btw


// Indices into names(). The patch resolves entries by placeholder text, not position.
private static final int N_MARKER = 0;
private static final int N_VM_STATE_FIELD = 1; // 5Ur -> A0Q (StateFlow-like, getValue() -> state)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a good idea to based on the indexes, as they can change in the future

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now the resolved names go into a Names record once, so nothing downstream keys off array position, and each placeholder is rewritten by matching its sentinel string rather than an index

@Stars6199

Copy link
Copy Markdown

I need this asap

@swakwork

Copy link
Copy Markdown
Collaborator

I need this asap

Then review it asap

/** Reads the Media backing the instant currently shown, via the live VM. Null on any mismatch. */
private static Object currentInstantMedia(Object vm) {
try {
Object stateHolder = declaredField(vm, NAMES.vmStateField); // LX/EuU

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your Entity class instead of Reflection

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry about the partial job on the entity conversion .... I only moved the media ->urls/username/type extraction onto the entity and left the vm -> state -> item -> media walk still on raw reflection.... i will do it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I want to check before redoing it though — the entity helper only reads fields. But the path to reach the Media object isn't all fields: in the middle it has to call two methods (getValue() and the state getter), and the entity helper has no way to call a method, it only reads fields. So this walk can't be fully expressed as an entity the way the media extraction was. Do you want me to still push it into the entity pattern and work around the two method calls, or is it fine to leave those two steps as plain reflection?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entity class does have getMethod which can be used for calling method using method name,
Like:

return (boolean) super.getMethod(this.obj, "methodName");

@Stars6199

Copy link
Copy Markdown

I need this asap

Then review it asap

Aight, i will wait :p

@swakwork

Copy link
Copy Markdown
Collaborator

Hey, can you test the PR on 439 ?

Passively captures view-once Instants (quicksnap) as they're viewed into a local vault, so they can be re-viewed and downloaded afterwards from a new "Instants" settings screen. The viewer's obfuscated VM/state/item/Media names are resolved at patch time from the target dex (§11) and the whole feature installs all-or-nothing — an Instagram-side miss disables only this feature, never the session.

- InstantsDownloadHook: capture-on-view into PikoInstantsDb (dedup by id, stores a stable userid alongside username)
- InstantsVaultActivity: thumbnails grouped by user, tap to download/open/copy
- Registered via its own resource patch, pulled only when the patch applies
…a walk now goes fully through the Entity pattern (getField + getMethod)
Capture used to poll the viewer's state and take its first item, so nothing
was saved while that list was empty and only one Instant per sender was saved
when it wasn't. It now hooks the constructor of the app's per-instant item,
which is handed that Instant's Media directly - every one is caught as it is
built, and no obfuscated names are left in the runtime path.

Also adds a username search to the saved Instants screen, drops expired links
instead of leaving blank tiles, keeps thumbnails between visits, and shows
when each Instant was saved.
@vishnuprasad0
vishnuprasad0 force-pushed the feat/instants-download branch from 2cfa367 to 22e4162 Compare August 15, 2026 18:07
The grid loaded every thumbnail up front, which ran the heap out on a
large vault. Tiles now load only near the viewport and dead rows are
dropped in one pass.
@vishnuprasad0

vishnuprasad0 commented Aug 16, 2026

Copy link
Copy Markdown
Author

Hey, can you test the PR on 439 ?

Yes,, tested on 439 works

# Conflicts:
#	extensions/instagram/src/main/java/app/morphe/extension/instagram/settings/Settings.java
#	extensions/instagram/src/main/java/app/morphe/extension/instagram/settings/SettingsStatus.java
#	extensions/instagram/src/main/java/app/morphe/extension/instagram/settings/preference/widgets/ButtonPref.java
#	patches/src/main/resources/addresources/values/instagram/strings.xml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants