EXP-069 — GENERIC INTERACTION RUNTIME + TELEGRAM LOGIN FLOW CAMPAIGN
Problem:
EXP-068 proved the Login UI with a visible Next button, but there was no way to interact with the UI — no text input dispatch, no click dispatch. The runtime could render a static screenshot but couldn't simulate user interaction.
Evidence:
| Metric |
EXP-068 |
EXP-069 |
| Text input dispatch |
not implemented |
DISPATCHED (view=2827, text='+15551234567') |
| Click dispatch on Next button |
not implemented |
DISPATCHED (view=3869, listener=3897) |
| onNextPressed called |
NO |
YES (10 times) |
| ConnectionsManager.sendRequest called |
NO |
YES (auth.sendCode boundary reached) |
| Phone field text in view_tree |
empty |
'+15551234567' |
| Text-bearing ViewNodes |
49 |
50 (+1 from phone input) |
| login_ui.png SHA256 |
39ca3cd7... |
064b82f2... |
| 3-run reproducibility |
identical |
identical |
| Generic regression |
PASS |
PASS |
Root cause:
No generic interaction subsystem existed. The runtime could execute DEX bytecode and capture View state, but had no API to inject user events (text input, clicks) into the View hierarchy.
Fix:
Phase 2 — Generic text input API
- Added
dispatch_text_input(view_id, text) to DalvikExecutionEngine.
- Dispatches to
ViewShadow.setText via the shadow registry — the SAME path that DEX setText uses.
- Stores text on the ViewNode, making subsequent
getText() calls in DEX bytecode return the new value.
- Added
dispatch_text_input_by_class(class_substring, text) convenience wrapper.
- GENERIC — no Telegram-specific code. Works for any EditText/TextView.
Phase 6 — Click dispatch integration
dispatch_click(view_id) already existed from EXP-060 (used to create LoginActivity by clicking the Start Messaging button).
- Added integration: after LoginActivity is created, the runtime now:
- Injects phone number
'+15551234567' into PhoneView$3 (phoneField EditText) via dispatch_text_input_by_class.
- Dispatches click on
FragmentFloatingButton (Next button) via dispatch_click_by_class.
- The click reaches the REAL Telegram callback path:
- Listener class:
LoginActivity$$ExternalSyntheticLambda3 (lambda/synthetic class)
- Callback method:
onClick(View) dispatched via try_recursive_invoke
onNextPressed called 10 times (real DEX bytecode executed)
ConnectionsManager.sendRequest called (auth.sendCode network boundary reached)
Interaction trace
[UI-EVENT] event=TEXT_INPUT view_object=2827 view_class=Lorg/telegram/ui/LoginActivity$PhoneView$3 old_text="" new_text="+15551234567"
[UI-EVENT] event=TEXT_INPUT result=DISPATCHED view=2827 text="+15551234567"
[EXP069] Text input result: DISPATCHED
[EXP069] Phase 6: Clicking FragmentFloatingButton...
[UI-EVENT] event=CLICK view_object=3869 view_class=Lorg/telegram/ui/Components/FragmentFloatingButton listener=3897 listener_class=Lorg/telegram/ui/LoginActivity$$ExternalSyntheticLambda3
[UI-EVENT] event=CLICK result=DISPATCHED listener=Lorg/telegram/ui/LoginActivity$$ExternalSyntheticLambda3
[EXP069] Click result: DISPATCHED
Generic impact:
dispatch_text_input is GENERIC — it dispatches to ViewShadow.setText, which is the same path DEX bytecode uses. Any Android app's EditText can receive text input this way.
dispatch_click is GENERIC — it invokes the real OnClickListener.onClick(View) via try_recursive_invoke, which executes the real DEX bytecode of the listener's class (including synthetic/lambda classes).
- The interaction flow (text input → click → callback → network boundary) is a generic Android interaction pattern, not Telegram-specific.
Tests:
- 3-run reproducibility: identical SHA256 (
064b82f2582faf7624f9a6d8f755bee48c4d4d78be115a81fe62fd7e055b1ead)
- Generic regression (synthetic Acme app): PASS — match_rate=1.0
- OCR: all 3 expected strings detected (Please confirm..., Phone number, Country)
- Phone field text verified in view_tree.json:
'+15551234567'
- onNextPressed called 10 times (real DEX bytecode executed)
- ConnectionsManager.sendRequest called (auth.sendCode boundary reached)
Before/after:
|
Before (EXP-068) |
After (EXP-069) |
| Text input |
not possible |
phone number injected |
| Click on Next |
not possible |
click dispatched to real listener |
| onNextPressed |
not called |
called 10 times |
| sendRequest |
not called |
called (auth.sendCode boundary) |
| Phone field text |
empty |
'+15551234567' |
| login_ui.png |
static screenshot |
shows injected phone number |
Memory:
Runtime RSS ~519 MB (unchanged). The interaction subsystem adds negligible memory (just the dispatch methods, no new data structures).
Image result:
The login_ui.png now shows the phone number +15551234567 rendered in the phone field (previously empty). The image is 27KB (was 22KB — the additional text content increases the PNG size).
Checkpoint:
CHECKPOINT_L_LOGIN_UI = PROVEN (maintained, with interactive text input + click dispatch)
Network boundary status (STUB_DEBT):
ConnectionsManager.sendRequest is called by the real onNextPressed bytecode
- The native network call (
native_sendRequest) is STUBBED — no real Telegram server request
- The
RequestDelegate callback does NOT fire because no response is returned
- This is the controlled network boundary (Phase 11 — TODO)
- Classification:
CONTROLLED_NETWORK_STUB — NOT real Telegram networking
Remaining blockers:
- Controlled mock network response (Phase 11/12/13) — intercept sendRequest, produce mock TL_auth_sentCode, deliver through real RequestDelegate callback
- Page state transition (Phase 14) — setPage(VIEW_CODE_SMS) not yet reached
- SMS View discovery (Phase 15) — SmsView not yet visible
- SMS screenshot (Phase 18) — login_sms.png not yet generated
- TextWatcher callback dispatch (Phase 3) — not yet implemented
- Real measure/layout engine (Phase 2 from EXP-068 spec)
Final commit: fd95856 — EXP-069 Phase 2+6: Generic text input + click dispatch
The image is the gate. The pixels show the injected phone number. The click reached the real callback path (onNextPressed → sendRequest). ✅
STUB_DEBT:
NETWORK: controlled auth.sendCode response — NOT real Telegram server communication
NATIVE: native_sendRequest stubbed — no real MTProto
THREAD: deterministic main-thread model — not full Android scheduling
EXP-069 — GENERIC INTERACTION RUNTIME + TELEGRAM LOGIN FLOW CAMPAIGN
Problem:
EXP-068 proved the Login UI with a visible Next button, but there was no way to interact with the UI — no text input dispatch, no click dispatch. The runtime could render a static screenshot but couldn't simulate user interaction.
Evidence:
Root cause:
No generic interaction subsystem existed. The runtime could execute DEX bytecode and capture View state, but had no API to inject user events (text input, clicks) into the View hierarchy.
Fix:
Phase 2 — Generic text input API
dispatch_text_input(view_id, text)toDalvikExecutionEngine.ViewShadow.setTextvia the shadow registry — the SAME path that DEXsetTextuses.getText()calls in DEX bytecode return the new value.dispatch_text_input_by_class(class_substring, text)convenience wrapper.Phase 6 — Click dispatch integration
dispatch_click(view_id)already existed from EXP-060 (used to create LoginActivity by clicking the Start Messaging button).'+15551234567'intoPhoneView$3(phoneField EditText) viadispatch_text_input_by_class.FragmentFloatingButton(Next button) viadispatch_click_by_class.LoginActivity$$ExternalSyntheticLambda3(lambda/synthetic class)onClick(View)dispatched viatry_recursive_invokeonNextPressedcalled 10 times (real DEX bytecode executed)ConnectionsManager.sendRequestcalled (auth.sendCode network boundary reached)Interaction trace
Generic impact:
dispatch_text_inputis GENERIC — it dispatches to ViewShadow.setText, which is the same path DEX bytecode uses. Any Android app's EditText can receive text input this way.dispatch_clickis GENERIC — it invokes the real OnClickListener.onClick(View) viatry_recursive_invoke, which executes the real DEX bytecode of the listener's class (including synthetic/lambda classes).Tests:
064b82f2582faf7624f9a6d8f755bee48c4d4d78be115a81fe62fd7e055b1ead)'+15551234567'Before/after:
Memory:
Runtime RSS ~519 MB (unchanged). The interaction subsystem adds negligible memory (just the dispatch methods, no new data structures).
Image result:
The
login_ui.pngnow shows the phone number+15551234567rendered in the phone field (previously empty). The image is 27KB (was 22KB — the additional text content increases the PNG size).Checkpoint:
CHECKPOINT_L_LOGIN_UI = PROVEN(maintained, with interactive text input + click dispatch)Network boundary status (STUB_DEBT):
ConnectionsManager.sendRequestis called by the real onNextPressed bytecodenative_sendRequest) is STUBBED — no real Telegram server requestRequestDelegatecallback does NOT fire because no response is returnedCONTROLLED_NETWORK_STUB— NOT real Telegram networkingRemaining blockers:
Final commit:
fd95856—EXP-069 Phase 2+6: Generic text input + click dispatchThe image is the gate. The pixels show the injected phone number. The click reached the real callback path (onNextPressed → sendRequest). ✅
STUB_DEBT:
NETWORK: controlled auth.sendCode response — NOT real Telegram server communicationNATIVE: native_sendRequest stubbed — no real MTProtoTHREAD: deterministic main-thread model — not full Android scheduling