generated from exteraGramPlugins/pluginBase
Rename to Extended Media Forwards and forward rich message media
Publish plugin / publish (push) Successful in 5s
Publish plugin / publish (push) Successful in 5s
Rename the plugin (id extended_media_forwards) and add the Forward button to media opened from rich messages, both single media and carousels. Rich message media opens PhotoViewer through ChatActivity's ChatArticlePageBlocksAdapter, whose parent object is the MessageObject rather than a WebPage, so the article-only check hid the button. The re-sent media links back to the message when it lives in a channel or supergroup; elsewhere it is sent without a caption. FileLoader now gets the parent object so expired file references can be refreshed. Instant View carousels already worked: slideshow items are flattened into the article's photo list. Findings are in docs/media-viewer-notes.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
05c5212a8a
commit
46503f24a0
9 files changed
+317
-36
No files matched your search
@@ -0,0 +1,100 @@
|
||||
# Media viewer internals — findings
|
||||
|
||||
Notes on how exteraGram opens page-block media, gathered by decompiling the
|
||||
12.10.6 APK (versionCode 71129) with jadx and testing on a device. Class and
|
||||
field names are from that build. They are app internals, not SDK API, so check
|
||||
them again after app updates.
|
||||
|
||||
## Two adapters, one PhotoViewer
|
||||
|
||||
Page-block media never opens with a `MessageObject` list. PhotoViewer runs on a
|
||||
`PhotoViewer.PageBlocksAdapter` instead, and `onPhotoShow` fires the same way
|
||||
for both callers below, so one hook covers them.
|
||||
|
||||
| Opened from | Adapter | `getParentObject()` |
|
||||
|---|---|---|
|
||||
| Instant View article (`ArticleViewer.openPhoto`) | `ArticleViewer.RealPageBlocksAdapter` | `TLRPC.WebPage` |
|
||||
| Rich message in a chat (`ChatActivity.openArticlePhoto`) | `ChatActivity.ChatArticlePageBlocksAdapter` | the message's `MessageObject`, or the bare `TL_iv.RichMessage` when constructed without one |
|
||||
|
||||
Both adapters expose the same `getMedia(i)` / `getFile(i)` / `isVideo(i)`. Those
|
||||
methods resolve through `ArticleViewer.WebPageUtils`, which has overloads for
|
||||
`WebPage`, `TL_iv.Page` and `TL_iv.RichMessage`. That is why `ivmedia.media_at`
|
||||
works unchanged for both. Only the parent object tells them apart.
|
||||
|
||||
A rich message is recognised by `MessageObject.messageOwner.rich_message != null`.
|
||||
|
||||
## Carousels (slideshows)
|
||||
|
||||
- **Articles:** `WebpageAdapter.addAllMediaFromBlock` flattens every
|
||||
`pageBlockSlideshow` / `pageBlockCollage` item into `photoBlocks`. Tapping a
|
||||
slideshow item therefore goes through `RealPageBlocksAdapter` like any other
|
||||
article photo, and needed no special handling.
|
||||
- **Rich messages:** `RichMessageLayout.collectMediaBlocks` flattens single
|
||||
media and slideshow items into one list for `ChatArticlePageBlocksAdapter`.
|
||||
Swiping in the viewer walks through all of them, and
|
||||
`updateSlideshowCell` keeps the in-chat carousel in sync.
|
||||
|
||||
## telegra.ph cannot produce a carousel
|
||||
|
||||
Telegraph has no gallery/slideshow node. The editor has no such block, and
|
||||
`createPage` keeps only the first `<img>` of a `<figure>` holding several, so
|
||||
consecutive images render as separate `pageBlockPhoto`s. Carousels only reach
|
||||
the viewer from other Instant View templates (sites other than telegra.ph,
|
||||
covered by the `any_article` setting) and from rich messages.
|
||||
|
||||
`telegra.ph/upload` no longer accepts files ("Unknown error"). Test pages have
|
||||
to reference external image URLs, e.g. on `upload.wikimedia.org`.
|
||||
|
||||
## Rich messages need Premium to send media
|
||||
|
||||
The rich composer is the attach menu's "Article" tab (`ChatAttachAlertRichLayout`).
|
||||
Sending is locked unless `MessagesController.richEditorAllowed()` or
|
||||
`UserConfig.isPremium()`, whenever the content is "lossy"
|
||||
(`RichMessageConvert.isLossy`). Lossy means anything a plain message can't
|
||||
represent, and every media block is lossy. A non-Premium account therefore
|
||||
cannot send a test rich message with media.
|
||||
|
||||
To test without one, build the message locally and open it the same way
|
||||
`ChatActivity.openArticlePhoto` does:
|
||||
|
||||
```python
|
||||
rm = TL_iv.RichMessage() # fill rm.photos and rm.blocks
|
||||
msg = TLRPC.TL_message() # peer_id/id/dialog_id of any chat
|
||||
msg.rich_message = rm
|
||||
mo = MessageObject(account, msg, False, False)
|
||||
adapter = ChatActivity.ChatArticlePageBlocksAdapter(rm, flat_blocks, mo)
|
||||
viewer = PhotoViewer.getInstance()
|
||||
viewer.setParentActivity(chat_fragment)
|
||||
viewer.openPhoto(index, adapter, PhotoViewer.EmptyPhotoViewerProvider())
|
||||
```
|
||||
|
||||
Real `TLRPC.Photo` objects with server locations can be borrowed from the
|
||||
`cached_page.photos` of any loaded article. Pointing `peer_id` at a channel
|
||||
exercises the message-link caption.
|
||||
|
||||
`ArticleViewer.open(TL_webPage, anchor)` re-requests `messages.getWebPage` and
|
||||
replaces the page with the server copy. Copy the original `hash` onto the
|
||||
injected `TL_webPage` to keep a locally modified `cached_page`.
|
||||
|
||||
## Message links
|
||||
|
||||
Only channels and supergroups (`ChatObject.isChannel`) have message links:
|
||||
`https://t.me/<username>/<id>` when public, `https://t.me/c/<chat id>/<id>`
|
||||
otherwise. On Android the dialog id of a channel is `-chat.id`, with no `-100`
|
||||
prefix. DMs, basic groups and Saved Messages have no link, so media forwarded
|
||||
from rich messages there is sent without a caption.
|
||||
|
||||
## File references
|
||||
|
||||
Pass the adapter's parent object (WebPage or MessageObject) as `parentObject`
|
||||
to `FileLoader.loadFile`. `FileRefController` uses it to refresh an expired
|
||||
file reference, and it handles rich messages explicitly.
|
||||
|
||||
## Device debugging
|
||||
|
||||
Logcat can be completely silent when the device's log buffer is disabled in
|
||||
developer options, and `scripts/logs.sh` then shows nothing. A plugin can
|
||||
still write diagnostics to
|
||||
`ApplicationLoader.applicationContext.getExternalFilesDir(None)`, which maps
|
||||
to `/sdcard/Android/data/com.exteragram.messenger/files/` and is readable with
|
||||
`adb shell cat` without `run-as`.
|
||||
Reference in new issue
Block a user