generated from exteraGramPlugins/pluginBase
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>
93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
"""Opens the client's own "Share / Forward to" sheet and reports the chosen chats.
|
|
|
|
`ShareAlert` is the sheet the app itself uses for forwarding, so reusing it keeps
|
|
the picker pixel-identical to the stock one. It is constructed with no payload
|
|
(`sendingMessageObjects`, `sendingText` and `sendingFile` all null), which makes
|
|
its send path a no-op: it selects chats, fires `ShareAlertDelegate.didShare()`
|
|
and dismisses. The selection is read back off the (protected) `selectedDialogs`
|
|
field and the comment off `commentTextView`.
|
|
"""
|
|
|
|
from android_utils import log, run_on_ui_thread
|
|
from hook_utils import get_private_field
|
|
|
|
_delegate_class = None
|
|
|
|
# ShareAlert's own constant for "keep the keyboard out of the way".
|
|
_SOFT_INPUT_ADJUST_NOTHING = 48
|
|
|
|
# PhotoViewer hands focus over after AdjustPanLayoutHelper.keyboardDuration,
|
|
# which is 250ms in the app's animation constants.
|
|
_FOCUS_DELAY_MS = 250
|
|
|
|
|
|
def _make_delegate_class():
|
|
"""Builds the ShareAlertDelegate proxy lazily: the Java interface can only
|
|
be resolved once the app classes are loadable."""
|
|
from java import dynamic_proxy
|
|
from org.telegram.ui.Components import ShareAlert
|
|
|
|
class Delegate(dynamic_proxy(ShareAlert.ShareAlertDelegate)):
|
|
def __init__(self, alert, on_picked):
|
|
super().__init__()
|
|
self.alert = alert
|
|
self.on_picked = on_picked
|
|
|
|
def didCopy(self):
|
|
return False
|
|
|
|
def didShare(self):
|
|
self.on_picked(_selected_peers(self.alert), _comment(self.alert))
|
|
|
|
return Delegate
|
|
|
|
|
|
def _selected_peers(alert):
|
|
dialogs = get_private_field(alert, "selectedDialogs")
|
|
if dialogs is None:
|
|
return []
|
|
return [dialogs.keyAt(i) for i in range(dialogs.size())]
|
|
|
|
|
|
def _comment(alert):
|
|
field = get_private_field(alert, "commentTextView")
|
|
if field is None:
|
|
return ""
|
|
text = field.getText()
|
|
return str(text).strip() if text is not None else ""
|
|
|
|
|
|
def pick_chats(context, on_picked, resources_provider=None):
|
|
"""Shows the stock share sheet. `on_picked(dialog_ids, comment)` runs on the
|
|
UI thread once the user confirms."""
|
|
global _delegate_class
|
|
from org.telegram.ui.Components import ShareAlert
|
|
|
|
alert = ShareAlert(context, None, None, False, None, False, resources_provider)
|
|
|
|
if _delegate_class is None:
|
|
_delegate_class = _make_delegate_class()
|
|
# Keep a reference on the module so the proxy is not collected early.
|
|
delegate = _delegate_class(alert, on_picked)
|
|
alert.setDelegate(delegate)
|
|
pick_chats._alive = delegate
|
|
|
|
# ShareAlert opens over PhotoViewer's window. Taking focus immediately
|
|
# fights the viewer's own window animation, so the stock code in
|
|
# PhotoViewer.showShareAlert() starts unfocused and hands focus over once
|
|
# the keyboard-transition window has passed; without that second step the
|
|
# comment field can be tapped but never receives key events.
|
|
alert.setFocusable(False)
|
|
alert.getWindow().setSoftInputMode(_SOFT_INPUT_ADJUST_NOTHING)
|
|
run_on_ui_thread(lambda: _enable_focus(alert), _FOCUS_DELAY_MS)
|
|
alert.show()
|
|
return alert
|
|
|
|
|
|
def _enable_focus(alert):
|
|
try:
|
|
if alert.getWindow() is not None:
|
|
alert.setFocusable(True)
|
|
except Exception as exc:
|
|
log("[extended_media_forwards] focus handoff failed: %s" % exc)
|