Skip to content

Fix QR scans and other device events not reaching web-view pages on Android - #613

Merged
simonhamp merged 1 commit into
NativePHP:mainfrom
bkuhl:fix/android-native-events-in-php-webviews
Oct 28, 2026
Merged

simonhamp merged 1 commit into
NativePHP:mainfrom
bkuhl:fix/android-native-events-in-php-webviews

Conversation

@bkuhl

@bkuhl bkuhl commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

The Android counterpart of #611, which fixes the same problem on iOS.

The problem

In a Livewire app on Android, tapping a "Scan QR code" button opens the scanner. It reads the code and closes, and then nothing happens. The page never hears about the scan, so it never navigates or shows anything. There's no error anywhere.

This happens to any web-view app set up the way the v4 docs describe: Route::native() to a screen with <webview php fullscreen />, and NATIVEPHP_START_URL pointing at it. It isn't limited to the scanner. No device event reaches these pages, so #[OnNative] listeners and Native.on() callbacks never fire.

Why

MainActivity.onNativeEvent is the only way device events reach a page. It returns early while a native screen owns the UI, so that the root page behind it doesn't get the event a second time. A php-mode <webview> is part of that native screen, so its page is skipped too.

Fix

  • WebViewManager.setup() records embedded web views (php-mode <webview> elements). They're held weakly.
  • onNativeEvent delivers each event to those web views. The root web view keeps its existing rule.

Each php-mode web view on screen gets the event, so PHP-side listeners run once per page, as with two browser tabs. #611 does the same on iOS.

Tested

On a Pixel Tablet emulator, with a Livewire app set up as the docs describe: Route::native('/app') to <native:webview php src="/" fullscreen />, and NATIVEPHP_START_URL=/app. The scanner's camera was stubbed to report a code.

  • Before: the scanner dispatches CodeScanned, and the page never receives it.
  • After: the scan reaches #[OnNative(CodeScanned::class)], and the page navigates.
  • With the start URL on a web route (the pre-v4 web view), events already reached the page. This PR doesn't change that path.

Workaround until this ships

The workaround in #611 also works on Android: the native screen receives the events, so handle them there with #[On]. We verified it on the same emulator.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@simonhamp

Copy link
Copy Markdown
Member

great

@simonhamp
simonhamp merged commit 189dd1b into NativePHP:main Oct 28, 2026
5 checks passed
gwleuverink added a commit that referenced this pull request Nov 2, 2026
Covers mobile PRs #525, #527, #528, #560, #605, #608, #609, #611, #613, #614, #615, #616, #617, #619, #621, #622, #623, #624, #625, #626, #627, #628, #629, #632, #633, #634, #635 and #636, and the commits on main without a PR: 4082538 (bounded()'s docblock), 49528ba (the Android logging fix from a fork), b23aba9, 2a773cc and b9316b6 (security policy, issue template and README links). Main b107ed9..767a7c8, 33 commits, 4.6.1 among them.

Ported to core on branch sync/mobile-main-2026-11-01, which this merge needs: #608 (scrollVirtualListTo()), #625 with 4082538 (snap points on a gesture area), #621 (:context-menu on a pressable, matched through core's ConvertsToElement as :menu is), #617's farewell frame through a hook, and #614's ValidatesInput and FieldErrors, which move to core. The old names of those two resolve to core's through the alias shim.

Kept on this branch: #616 in full. EVENT_SYSTEM_BACK sits on NativeComponent next to EVENT_HOT_RELOAD and EVENT_SHUTDOWN, the router half is in NativeRouter, and InteractsWithAppShell's back button handler checks the constant, as main's runLoop() does. #617's element types: NativeComponent answers core's typesClosedInFarewellFrame() hook with bottom_sheet and modal. BroadcastsGlobally extends core's marker and #609's DispatchableFromNative.

Removed again in the merge: HasVirtualListWindow, GestureArea and Pressable (main changed them, core has them), and ValidatesInput and FieldErrors (main added them, core has them). Main's import of the old dispatcher name came out of DispatchEventFromAppController, where it clashed with core's.

Adapted for the extraction: BroadcastsGloballyNamesTest expects DispatchableFromNative among the old marker's interfaces, as #609 makes it.

#609 is merged as main has it and is not ported.

Needs NativePHP/core#18.

Tests, all on PHP 8.4.27, with dev dependencies updated for #628 (Laravel 12.70.2, Pest 3.8.7): mobile on this merge against the core sync branch (445049b), 2,467 passed, 4 risky, 4 skipped. PHPStan clean. Mobile main (767a7c8), 2,412 passed, 4 risky, 4 skipped. Desktop main (fd40580) against the core sync branch, test:php 2,240 passed, as against core main. Desktop #57 (977a2af), 2,245 passed against the earlier core sync head (59c73fc), which differs only by the constant moved to mobile; not rerun.
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.

2 participants