Android port refactor: modular runtime/JNI, unified rendering modes, and hosted CI#3
Android port refactor: modular runtime/JNI, unified rendering modes, and hosted CI#3ppigazzini wants to merge 46 commits into
Conversation
Run simulator tests and Android debug packaging in parallel on github_ci, pull requests, manual dispatch, and main. Resolve one authoritative C43 core revision per workflow run, sync that revision into both jobs, build the pinned xlsxio toolchain at run time, upload the debug APK with ABI and 16 KB packaging evidence, and let manual runs on main publish the synced-core snapshot release.
Extract audio, slot, work-directory, physical keyboard, keypad, runtime, storage-access, and display-action ownership from MainActivity into focused helpers. Tighten the JNI bridge and native storage path handling, align SettingsActivity and rebuild docs with the shared Android shell contracts, and update the Android build wiring for 16 KB pages, Java discovery, and staged mini-gmp fallback.
Replace build-time staged-source rewrites with an explicit Android native staging step, add tracked GTK, GDK, and Cairo stub headers, derive the About version summary from a Gradle property, and align the maintainer and rebuild docs with the live staging contract.
Split the Android JNI bridge into lifecycle, input, display, and storage source files while keeping the existing RegisterNatives contract. Reuse one keyboard-state snapshot per dynamic refresh pass so the keypad no longer asks native for keyboard state once per key.
Keep the checked-in namespace and applicationId aligned, retain the property-backed version and snapshot contract, trim the debug APK to arm64-v8a, and drop the stale useLegacyPackaging fallback now that AGP 8.7.3, NDK 29, and CI alignment checks cover the current packaging path.
Expose one native keypad snapshot for the Android-native keypad, reuse it across each refresh pass instead of polling labels individually, and make the native keypad the default interaction path while keeping the existing simulator-aligned input bridge.
Widen the existing keypad snapshot so Android receives per-key style metadata, softkey graphics state, dotted-row cues, and function-preview state from native code while keeping the current JNI entry points stable for the existing refresh loop.
Remove the runtime skin toggle and photographic keypad branch, draw native shell chrome in the overlay, switch the main keypad renderer to scene-driven styles and calculator font roles, and make dynamic relabeling the default keypad contract while updating the Phase 6 maintainer docs.
Render all 43 keys from the native keypad snapshot, promote F1-F6 to visible softkey cards, align key geometry and color treatment with the Windows simulator, and document the broader Android-native keypad surface in README.
Lock keypad typography to the simulator CSS sizes, add the simulator-style top highlight stroke, restyle F1-F6 to the simulator dark-key palette, and update the native-keyboard docs for the landed renderer work.
Add a public Android development doc tree under android/docs/dev for the checked-in shell. Document the build and staging contract, Kotlin runtime structure, native core and JNI boundary, scene-driven UI rendering, GTK-to-Android mapping rules, the CI lane, and curated official references.
Use the live overlay projection for LCD long-press hit testing so display actions follow the current shell geometry. Queue core tasks as soon as the app is running, clear stale tasks on full shutdown, make JAVA_HOME resolution portable across macOS and non-GNU environments, and drop the unused fg key background resource.
Drop the unused Android-local r47 shell images, remove the stale build-script note about those drawables, and update the maintainer keyboard docs to keep the Android-native shell cleanup separate from the upstream GTK simulator PNG mockup path.
Add the upstream-driven Windows simulator workflow for UCRT64 and CLANG64, refresh the Android and Windows cache setup, and make the hosted make-to-Ninja path explicit with job-safe parallel flags. This also fixes the Android SDK cache restore path on hosted runners, threads parallel build settings through build_android.sh, and removes the two Kotlin unused-parameter warnings reported by the Android CI log.
Normalize R47_BUILD_JOBS and CMAKE_BUILD_PARALLEL_LEVEL, fall back to the host CPU count when neither is set, export one explicit worker count for make, Ninja, and Gradle, and align the build docs references with that contract.
Restore r47_texture, r47_background_v2, and r47_black_edition, make r47_texture the default chrome_mode, keep native chrome available, and split the shell runtime between the restored classic texture hit-zone path and the newer scene-driven keypad shells.
Use one shared touch-cell map for texture, native, and mixed shell modes, keep the existing scene-driven key renderer, and make the debug overlay show the actual touch targets instead of every child view bound. Update documentation.
There was a problem hiding this comment.
Pull request overview
This PR is a broad refactor of the Android port build + staging pipeline, introduces new Android shell chrome modes and supporting Kotlin/native infrastructure, and adds CI workflows + developer documentation to codify the new contracts.
Changes:
- Refactors Android build entrypoint (
build_android.sh) to be more deterministic (tool detection, parallelism, pinned xlsxio provisioning) and moves native staging into a dedicated script. - Adds/updates Android shell features:
chrome_modepreference, updated overlay projection logic, scene-driven keypad layout, physical keyboard mapping, SAF/work-directory coordination, and native JNI/HAL glue for lifecycle + storage. - Updates generators/build plumbing (Meson + Makefile) to produce/stage an additional generated source, and adds GitHub Actions workflows + extensive Android dev docs.
Reviewed changes
Copilot reviewed 44 out of 57 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/generateConstants/meson.build | Adds constantPointers2.c to generator outputs for downstream staging/build use. |
| build_android.sh | New robust build orchestration: Java/SDK detection, job count normalization, xlsxio provisioning, staging delegation, Gradle property wiring. |
| android/stage_native_sources.sh | New script to stage canonical core + generated outputs + mini-gmp into android/app/src/main/cpp. |
| android/docs/dev/README.md | Adds Android dev doc index + ownership rules. |
| android/docs/dev/90-official-references.md | Adds curated upstream references for Android/Kotlin/NDK/CI/storage. |
| android/docs/dev/40-ui-rendering-and-gtk-mapping.md | Documents overlay/keypad rendering model and GTK-derived geometry mapping. |
| android/docs/dev/30-native-core-and-jni.md | Documents JNI registration, threading model, file I/O boundary, packaging/16KB contract. |
| android/docs/dev/20-kotlin-shell-architecture.md | Documents Kotlin shell class ownership, runtime flow, lifecycle rules. |
| android/docs/dev/10-build-and-source-layout.md | Documents build entrypoints, staging model, and CI lane behavior. |
| android/docs/README.md | Adds top-level pointer to Android dev docs subtree. |
| android/app/src/main/res/xml/root_preferences.xml | Replaces skin settings with chrome_mode; routes About version summary through a string resource. |
| android/app/src/main/res/values/arrays.xml | Renames skin arrays to chrome arrays; updates scaling label copy. |
| android/app/src/main/res/drawable-xhdpi/r47_background.webp | Adds density-qualified background asset for r47_background mode. |
| android/app/src/main/res/drawable-mdpi/r47_background.webp | Adds density-qualified background asset for r47_background mode. |
| android/app/src/main/res/drawable-hdpi/r47_background.webp | Adds density-qualified background asset for r47_background mode. |
| android/app/src/main/java/com/example/r47/WorkDirectory.kt | Centralizes SAF tree URI persistence/validation + subfolder resolution. |
| android/app/src/main/java/com/example/r47/StorageAccessCoordinator.kt | Adds coordinator for SAF flows, first-run prompting, and work-dir validation UX. |
| android/app/src/main/java/com/example/r47/SlotStore.kt | Introduces slot persistence model/constants for shared prefs usage. |
| android/app/src/main/java/com/example/r47/SettingsActivity.kt | Updates settings to use WorkDirectory/SlotStore constants and shared helpers. |
| android/app/src/main/java/com/example/r47/ReplicaOverlay.kt | Refactors overlay projection and adds chrome-mode specs, bitmap caching, and LCD hit-testing. |
| android/app/src/main/java/com/example/r47/ReplicaKeypadLayout.kt | Adds normalized touch grid + dynamic keypad rendering for non-texture chrome modes. |
| android/app/src/main/java/com/example/r47/PhysicalKeyboardInput.kt | Adds physical keyboard mapping + shortcut sequencer. |
| android/app/src/main/java/com/example/r47/NativeCoreRuntime.kt | Adds shared core thread/task queue + frame-loop LCD/keypad refresh coordination. |
| android/app/src/main/java/com/example/r47/KeypadSnapshot.kt | Adds scene-contract snapshot model for scene-driven keypad rendering. |
| android/app/src/main/java/com/example/r47/KeyboardStateSnapshot.kt | Adds typed keyboard-state snapshot parsing helpers. |
| android/app/src/main/java/com/example/r47/DisplayActionController.kt | Adds display long-press actions (clipboard + PiP) tied to LCD hit-test. |
| android/app/src/main/java/com/example/r47/AudioEngine.kt | Adds dedicated audio thread/queue for tone playback. |
| android/app/src/main/cpp/c47-android/stubs/gtk/gtk.h | Adds GTK stub header include path via Android mocks. |
| android/app/src/main/cpp/c47-android/stubs/gdk/gdk.h | Adds GDK stub header include path via Android mocks. |
| android/app/src/main/cpp/c47-android/stubs/cairo.h | Adds Cairo stub header include path via Android mocks. |
| android/app/src/main/cpp/c47-android/jni_storage.c | Adds native SAF bridge entrypoints and blocking request implementation. |
| android/app/src/main/cpp/c47-android/jni_lifecycle.c | Adds lifecycle JNI glue (init/tick/save/load/refresh/slot). |
| android/app/src/main/cpp/c47-android/jni_input.c | Adds JNI input dispatchers for sim keys, functions, and menus. |
| android/app/src/main/cpp/c47-android/jni_bridge.h | Adds bridge API surface + shared native globals declarations. |
| android/app/src/main/cpp/c47-android/hal/lcd.c | Minor formatting adjustment for JNI signature readability. |
| android/app/src/main/cpp/c47-android/hal/io.c | Refactors Android base path handling and adds readiness checks before IO. |
| android/app/src/main/cpp/CMakeLists.txt | Adds stubs include dir; removes raw linker flag in favor of flexible page sizes arg. |
| android/app/build.gradle | Moves versioning to properties, sets debug suffixing, forces arm64 ABI, enables flexible page sizes, and injects about-version string. |
| android/REBUILD_MASTER_GUIDE.md | Updates rebuild guide to match new property-backed/versioning/staging and packaging contracts. |
| README.md | Updates repo overview + Android build/configuration instructions to match new contracts. |
| Makefile | Adds Ninja flags plumbing, second expansion use, and installs constantPointers2.c. |
| .github/workflows/windows-ci.yml | Adds Windows simulator CI lane with upstream sync and artifact packaging. |
| .github/workflows/android-ci.yml | Adds Android CI lane (upstream resolution, simulator tests, debug APK build, packaging evidence, optional main prerelease). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace the single texture PNG with lossless WebP drawables in mdpi through xxxhdpi buckets so Android resolves the closest shell asset without changing the existing chrome-mode contract.
Check JNI thread attach and Java exceptions in requestAndroidFile so native file requests fail cleanly instead of hanging, remove the redundant in-function typeDefinitions include from jni_lifecycle.c, and add a clear non-Debian failure path for the local minizip bootstrap in build_android.sh.
Close the ParcelFileDescriptor wrapper after detachFd() in the SAF native file picker, and switch the build script's JAVA_HOME, ANDROID_SDK_ROOT, ANDROID_HOME, and R47_NDK_VERSION reads to set-u-safe parameter expansion.
Run sleep-based physical keyboard shortcut sequences on a dedicated shortcut executor and queue each key or menu step back onto the core runtime so tick and LCD refresh continue during shortcut dispatch.
Rework CalculatorKeyView to render plain rounded key surfaces with a darker default dark-key fill, larger corner radii, and the corrected upper-row function-key primary legend anchor. Rework ReplicaOverlay to render the native calculator shell with a pure-black body, density-scaled top and bottom bars, and larger rounded shell corners while preserving keypad geometry, touch zones, LCD placement, and scene-driven content. Update README.md and android/docs/dev/40-ui-rendering-and-gtk-mapping.md so the tracked public docs match the live Kotlin rendering contract.
Use the real hardware F and G colors in the native keypad renderer and the Android settings theme. Preserve the upstream theme role split so the settings slider keeps distinct accent roles, remove the hover-only key palette from the touch app, and update the rendering docs so the live palette contract stays aligned with the code.
Align the Android keypad and settings palette to the hardware-derived F/G accents, then lift both colors by adding `10` to their HSL lightness channel so the orange and blue legends read slightly brighter. Retune the native shell body to `RGB(31, 31, 31)`, preserve the upstream theme role split, remove the hover-only key palette from the touch app, keep unassigned softkeys visually identical to assigned ones, restore stronger visible native pressed-state feedback for the F/G/FG keysand update the rendering docs so the live palette contract stays aligned with the code.
Drop the CLANG64 lane from the Windows workflow matrix so GitHub CI only builds the MSYS2 UCRT64 package. This keeps the hosted Windows coverage on the toolchain we still want to maintain and removes duplicate artifact work.
Generate COPYING and SOURCE from the canonical repo metadata so the APK and desktop archives carry the GPL text plus a repository URL and exact commit for the corresponding source. Expose the packaged GPL text and Android source link from Settings/About, and document the build-time source URL and commit contract for distributors.
Compose standard softkey legends in the native keypad scene using the existing simulator figlabel contract so Android stops drawing detached aux and value runs. Keep the conversion-family menus on their existing split layout.
Adjust the Android-native shell body, main key idle fill, and reverse-video softkey fill to the requested darker-neutral values. Reuse the main-key pressed highlight for reverse softkeys and preserve existing renderer ownership and geometry.
Remove the Kotlin physical-softkey three-dot decorator driven by SCENE_FLAG_DOTTED_ROW so upper f and g softkeys no longer show the Android-specific dot artifact. Leave the native LCD softmenu path and JNI metadata contract unchanged.
Derive the native shell corner radius from the live shared-shell geometry instead of the over-large hard-coded value, and update the Android keypad text path to use scaled-text rendering flags that match the current official Paint guidance. Keep the keypad as live view-rendered text rather than introducing a bitmap upsample path.
Use the measured `1820 x 3403` reference canvas as the live layout space, rebase the retained Android chrome and label policy into that space, collapse redundant overlay and keypad staging, lock the resulting contract with Python geometry tests, and keep the renamed top-bezel settings tap field consistent in the overlay debug drawing path. Keep `f`/`g` labels and fourth labels as split even text-size tiers, lower the fourth-label X offset to the even app-tested value, set the key draw radius to `20`, and set the native shell radius to `80`. Remove the redundant softkey primary-text alias, collapse the duplicate diagonal strike-out vertical insets, and rename the remaining softkey overlay owners so their center anchors and shared mark extent are explicit. Refresh the public and maintainer rendering docs against the live Kotlin owners for projection, LCD placement, key colors, label offsets, softkey policy, and exported-but-undrawn softkey scene flags.
Default the Android shell chrome setting to the background-backed native renderer, surface selected LCD and scaling modes in preference summaries, and lower the default haptic intensity while keeping runtime fallbacks aligned with the preference XML contract.
Align the R47 Linux and Windows desktop package collectors with the shared desktop artifact contract by staging license and source manifests, writing package inventories and build metadata, and using the shared default artifact compression convention.
Project the upstream GTK faceplate substitution through the Android JNI label export so the up and down f-shift captions use the same calculator glyphs as the desktop simulator. Keep executable item names and Kotlin rendering unchanged.
Project main-key labels through item-aware native metadata so Android matches GTK for CPXj glyph swaps, AIM placeholder and case labels, the R47 ASN submenu caption, and Norm_Key_00 blank-carrier caption selection. Keep the Kotlin view on the existing TextView path, preserve the standard C47 primary font, and leave the reverted assigned-blank canvas detour out of the implementation.
…adata Run the Android snapshot workflow on a daily schedule only when the resolved upstream core revision has not already been published, and keep publication gated behind successful simulator tests and APK packaging. Stage keyed SOURCE metadata plus GPL and xlsxio license files in GitHub release assets, simulator artifacts, and APK assets so every shipped binary carries the same repository and commit provenance contract.
|
the CI daily checks for new upstream commit and publish a new tagged dev release, also published all the required license/source_info |
|
The starting version of Native was a replica of the Windows simulator, you can see here #3 (comment) , but after having added the Native+Background that become simply a duplicated, so I switched to a flat design (that I like way more to be honest). The whole keyboard layout is really configurable?!? On my repo I update the toolchain but I did not push the commits to this PR fearing to disrupt your local setup, let me know if you are fine with the updated SW. Hoping all went fine with you kids :) |
Oh, Once I am back I'll try to update my toolchain and see if it works. Let's see how i arrive tomorrow, if i'm not too tired I can progress on some things. it not, hopefully by Monday evening |
|
No issue, take your time :) I started adding unit tests for the Android App and added a new CI workflow with a simulator to run the tests, still fighting with some bugs, though. |
|
This is a massive contribution, thank you. Because of this, I’ve manually ported your high-value features directly into our existing architecture. Thanks again for the modern SDK heads-up |
|
Hello, I noticed the recent merge of the refactor was done via a manual file copy. I appreciate you getting the code :) But we have a bit of a technical issue now:
I’ve prepared a fix for you. I created a branch that contains the full history of the original branch, with your final adjustments added neatly on top. This preserves everyone's work and keeps the project technically sound. You can run these commands to update the main branch with the correct history. This requires a "force push" to overwrite the manual commit with the full history, but with the backup of main this is safe. |
|
@paletochen I made you already the owner of the commit on top that restore your workflow |
I am so sorry about that manual merge. I’ve followed your instructions and just force-pushed the full history back to main, with my recent fixes (constantPointers2, CI promotion, etc.) cherry-picked neatly on top. It looks much more professional now and your contributor stats should be back where they belong. Could you take a quick look and confirm everything looks as you expected? Thanks for being so cool about it and for the awesome refactor! |
|
Yes, we are aligned with a clean linear history, thank you so much :) https://github.com/paletochen/r47_android/network
|








This refactor is aimed at making the Android port easier to review, rebuild, and keep aligned with the upstream core. Shared calculator behavior remains rooted in the upstream core; the Android-specific work is being made more explicit and easier to maintain.
MainActivityis now mainly a coordinator, while runtime, storage, display actions, audio, slot persistence, work-directory handling, and physical-keyboard input moved into dedicated helpers; the native bridge is likewise split into lifecycle, input, display, and storage layers.NativeCoreRuntime, with one shared core thread, one task queue, frame-driven LCD refresh, and scene-driven keypad snapshot refresh.r47_texture,r47_background,native) on one normalized touch grid, one LCD projection model, and one shared settings strip, withReplicaOverlay,ReplicaKeypadLayout, andCalculatorKeyViewconsuming the same native snapshot data.r47_texture,r47_background, andnativemodes.android/stage_native_sources.sh, explicit 16 KB page-size support, and new Android development docs covering build, runtime, JNI, and rendering ownership.workflow_dispatchrebuilds.