Repository navigation
Complete Android application and windowing host - #169
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Android applications can now call
Application.Run(Form)through a registeredIWindowingPlatform,AndroidWindowImpland Activity-owned native host. Shared Form lifetime, owned popups/modals, recreation and the externally owned dispatcher run over the existing Skia, focus, validation, IME and accessibility stack.Final review fixed three reproduced regressions: repeated Show retired a live View's epoch, Back/recreation used construction rather than presentation order, and an older owner Hide could destroy a newer reentrant Show. Native cleanup also tolerates popups disposed during lifecycle callbacks and rejects queued Back from a retired host.
Detailed review, API inventory, all 56 paths, acceptance evidence and commands.
Before / After
Before:
Activity → AndroidAppHost → AndroidSkiaHostView → SkiaControlSurfaceassembled by the sample.After:
Application.Run → Form → Android window backend → Activity host → existing native Skia presentation.Window policy
One main Form per Activity host + owner-bound modal child Forms + reusable in-Activity popups. A second independent main Form or concurrent Activity host is explicitly rejected. This does not implement a desktop-style multiple top-level window manager.
External loop
Android owns its system main Looper. An explicit neutral
IExternallyOwnedDispatcherImplcapability makes Run establish lifetime, show the Form and return; subscriptions remain until Exit. Posts, timers and InvokeAsync use the main Handler. There is no nested Looper or worker UI thread; framework Exit finishes native presentation without quitting the system loop. Windows retains its controlled/blocking loop and TestHost keeps its existing semantics.Lifecycle / recreation
The process registry holds a weak Activity host, while the Activity owns its container, Views and bitmaps. The same Form/control tree survives recreation. Generation and presentation epochs retire old input, IME, provider and Back callbacks; native focus must be confirmed before reacquiring text input. Hide releases presentation; terminal Close cleans once. OpenForms, Load/Shown and all three lifetime modes remain shared.
Platform semantics
Main size and chrome belong to Android. Modal/popup sizes are clamped hints; geometry is confirmed by native layout. Arbitrary top-level Move, non-Normal WindowState and unsupported desktop setters reject. Screen describes the current Activity display;
AndroidViewis a borrowed JNI handle valid for its presentation. Safe area is applied once, matching painting, touch and PointToScreen; IME insets remain informational. Rendering stays software Skia.Input / focus / validation / IME
Reuse #160 ControlFocusScope, #161 validation, existing bindings and ControlTextInputHost. A borrowed surface router uses the actual Form adapter rather than another root. Revocable clients feed the existing Android InputConnection; no EditText or second document model. Regressions cover validation veto/session retention, Form preview changing binding focus and shortcut-owned KeyUp.
Popup / modal / Back
Popups retain reusable Hide/Show and shared text leases. ShowDialog disables its owner and completes the existing asynchronous DialogResult task, restoring owner input. Back follows popup → last shown modal → cancellable main close after native IME handling. API 33+ uses guarded OnBackInvokedDispatcher; earlier APIs use OnBackPressed. Instrumentation waits for native IME transition idle without retrying Back.
Accessibility
Native nodes project the canonical semantic tree, including ancestry around the omitted Form client-area peer. Shared accessibility semantics are unchanged. Window peer bounds already use physical screen pixels. Phase 3 observes the real native focus event after one action, without increasing timeouts or adding action retries.
Sample
Removed transitional AndroidAppHost.cs from the normal path. MainActivity uses AndroidWindowActivity and Application.Run. WindowsAppHost derives from the same MainForm, keeping only desktop settings. The public AndroidActivityHost remains an intentional alternative for custom Activity inheritance/lifecycle forwarding. No new dotnet-new/VS Android template is claimed; complete startup/template guidance is documented with the sample.
Tests
PreparedPlanDoesNotAllocatePerIntermediateFrame, 552 B vs 256 B; it passed in the first Debug run and Release. Exact causes remain unestablished; neither full Debug run is represented as wholly green.Native validation
Final focused runs after review corrections, using real Views/main Looper/InputConnection/Back/recreation:
Windowing includes five recreations, retired View/Activity collection, stale IME rejection, repeated Show rendering, nested modal order, reentrant owner Hide/Show, backgrounding with a popup, posts/timers/cancellation and the surviving system Looper after framework Exit. A locked/asleep-phone startup failure is retained separately; the unchanged test passed after the user unlocked it.
The earlier complete native matrix (95 emulator / 105 phone assertions) and ten physical Release Phase 3 repetitions are historical implementation-stage evidence. They were not repeated in full after review; the final focused results above qualify the changed paths. Manual checks are listed separately below.
Packaging
Debug standalone APK, Release APK and Release AAB: PASS. Release uses profiled Mono AOT, partial trimming (
SdkOnly), embedded assemblies, arm64+x64, minimum API 23 / target 36. Framework/backend/sample AOT libraries were verified in both APK and AAB. Native Release runs used the preserved final APK. No store/distribution signing or submission claim.Limitations
Acceptance and issue links
All six criteria are met within the documented bounded mobile policy; this does not claim production-wide qualification or universally green Debug tests.
Fixes #72
Related to #63, #60, #79, #46, #160 and #161. #60/#79/#46/#168 remain OPEN and are not closed by this PR.
Two commits preserve the feature and its documentation correction separately:
6d14a538291b144e616dafd2a7e69e1c2443b298— Complete Android application windowing host.72db978ec3d431c5577bb6f14a637bafdf6aa895— Fix stale Android windowing documentation.The documentation follow-up corrects obsolete manual-surface startup, missing Form/window host, lifetime/DPI/inset, accessibility/calendar and ownership claims in 16 Markdown files. All 13 original PR Markdown files were audited; repository search also identified six related current guides requiring small corrections. Dated implementation reports, historical baselines and original validation counts remain preserved. Production code and tests are unchanged by the second commit.
Follow-up checks: repository documentation validation passed for all 19 PR Markdown files; CommonMark local link checks passed for 224 links/images including 53 anchor links; release-documentation script tests passed 32 assertions; stale-claim search and
git diff --checkpassed. No full local build or 4097-test rerun was needed for this Markdown-only correction. Required CI for the new head is tracked separately; the prior head's green result does not qualify it.The implementation review covers its original 56 paths. The complete PR now contains 62 paths: 44 modified, 17 added, 1 deleted. No artifacts, APK/AAB, captures, logs, TRX, helper scripts, machine configuration or
.codexfiles are committed. PR remains open for a separate final merge review; no merge in this task.