Repository navigation
feat(android): v1.8.2 — multi-touch virtual controller + app icon - #160
Conversation
… PLAY_BUILD - Multi-touch virtual controller (VirtualController.kt): a single Canvas both draws an NES-001 pad styled per assets/RustyNES_Icon/rustynes.svg (nes_controller() geometry + palette: grey body, charcoal face plate, black cross D-pad, grey Select/Start pills, red racetrack logo, dark-red concave A/B, red labels, with pressed-state lighting) AND collects input through one awaitPointerEventScope loop that recomputes the full Buttons mask from ALL pressed pointers each event — giving simultaneous presses, D-pad diagonals (overlapping corner regions) and slide. Replaces the per-button detectTapGestures overlay that only registered one input at a time. Art + hit regions both derive from the measured size, so they resize + remap in lockstep across the Z Fold 7's cover/inner displays. - EmulatorHandle: OR a touchMask (virtual controller) with a keyMask (hardware gamepad) into the single late-latched P1 mask so the two input sources don't clobber each other. Verified on-device: the pad renders, the D-pad lights on press, and the press drives SMB. - App icon: real RustyNES icon (from assets/RustyNES_Icon/icon-1024.png) as the launcher icon — adaptive (foreground inset 72% over a #1C1F24 background) + legacy density mipmaps; drops the placeholder "RN" monogram vector. - PLAY_BUILD flag (default false): gate the freemium (8-min timer + persistence locks + Billing) so sideload/dev builds are full-featured; only the Play AAB (v1.8.6) flips it on. Per the deferred-Play-launch plan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…version bump - Light haptics on a new button press (LocalHapticFeedback) in the virtual controller. - App-icon fix: the adaptive foreground was inset 72%, so the launcher squircle (Samsung One UI) still clipped the icon's corner peripherals. Re-inset to 62% so the whole RustyNES icon (gear + controller + the four corner peripherals) sits inside the mask, with dark-background margin. Verified on the Z Fold 7. - Version bump 1.8.1 -> 1.8.2 (workspace + crate pins; Android versionCode 10802 / versionName 1.8.2); CHANGELOG [1.8.2]; README badge + row + BibTeX; STATUS header. README/STATUS also note the deferred Play launch (v1.8.6) and the PLAY_BUILD full-featured interim builds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…right
Modify the parametric generator (make_icon.py) and regenerate the icon set:
- "Rusty" cap height 27 -> 35 (now equal to "NES"), with its baseline kept at the
previous bottom (184 + 27/2 = 197.5 -> center 180), so it grows upward and
overlaps the cog behind it (intentional).
- "Rusty" nudged right by 1/8 of a character (one char = the 35px em, so +4.375).
Regenerated assets/RustyNES_Icon/ (rustynes.svg, rustynes.ico, icon-{16..1024}.png,
preview.png) via the committed PressStart2P.ttf, and re-derived the Android v1.8.x
launcher mipmaps from the new icon-1024.png (legacy full + adaptive 62%-inset
foreground so the squircle keeps the corner peripherals). Verified on the Z Fold 7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Haptics: Compose's LocalHapticFeedback(TextHandleMove) tick wasn't felt on the Z Fold 7 (too subtle / gated by system touch-vibration). Switch the virtual controller to the system Vibrator (VibratorManager.defaultVibrator on API 31+, else VIBRATOR_SERVICE) firing a short EFFECT_CLICK (oneShot fallback < API 29) on each new button press; add the VIBRATE permission. - Replace images/RustyNES_Logo-Icon.png (the README header logo) with the regenerated icon-512.png (new equal-size "Rusty"/"NES" wordmark). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New versions of the root README assets (Arch-Blueprint 1 & 2, Banner-Logo, Emu-Compare); the previous versions are preserved under images/archive/ with a -v1.5 suffix. (RustyNES_Logo-Icon.png was already refreshed to the new icon-512.)
The pointer loop recomputed the mask from event.changes each event, which could miss held pointers when a new finger went down (so pressing a 3rd button dropped the first two — e.g. SMB run+jump while moving). Now track every active pointer by PointerId in a map and recompute the mask from the full set, so arbitrarily many simultaneous presses (D-pad + B + A …) all register. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates RustyNES to version 1.8.2, introducing a multi-touch virtual NES controller, refreshing the app icon, and gating freemium features behind a new PLAY_BUILD flag. The review feedback highlights several critical improvements for the Android implementation: wrapping the touch input loop in a try-finally block to prevent stuck inputs upon gesture cancellation, synchronizing input mask updates to avoid race conditions between touch and hardware inputs, eliminating object allocations (like listOf, to pairs, and Paint instances) in the hot drawing path to prevent GC stuttering, and using the Float overload of hypot to avoid unnecessary double conversions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR ships the Android v1.8.2 release updates: it replaces the previous single-touch Compose button overlay with a multi-touch, Canvas-rendered virtual NES controller, introduces the real RustyNES launcher icon, gates freemium/Billing behavior behind a PLAY_BUILD flag, and bumps release/version documentation across the repo.
Changes:
- Add a new multi-touch
VirtualControllerCanvas that both draws the controller and computes a full pressed-buttons mask across all active pointers (with haptics). - Gate Play Billing + freemium behavior behind
BuildConfig.PLAY_BUILD(default false) so sideload/dev builds are full-featured. - Update icons, version numbers, and release documentation (README / STATUS / CHANGELOG + workspace versions).
Reviewed changes
Copilot reviewed 13 out of 50 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Bumps displayed version and expands v1.8.2 release notes in the version table and citation. |
| docs/STATUS.md | Updates the status header summary to v1.8.2 with the new input/controller + icon + PLAY_BUILD notes. |
| CHANGELOG.md | Adds the 1.8.2 changelog entry documenting the virtual controller, icon, and PLAY_BUILD gating. |
| Cargo.toml | Bumps workspace/crate versions to 1.8.2. |
| Cargo.lock | Updates locked crate versions to 1.8.2. |
| assets/RustyNES_Icon/rustynes.svg | Updates the icon wordmark geometry (“Rusty” resized/shifted). |
| assets/RustyNES_Icon/make_icon.py | Adjusts wordmark sizing/position parameters used to generate icon assets. |
| android/app/src/main/res/values/ic_launcher_background.xml | Changes launcher icon background color to match the new icon styling. |
| android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | Switches adaptive icon foreground reference to the mipmap foreground asset. |
| android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml | Same as above for the round adaptive icon. |
| android/app/src/main/res/drawable/ic_launcher_foreground.xml | Removes the old placeholder “RN” vector foreground. |
| android/app/src/main/java/com/doublegate/rustynes/VirtualController.kt | Adds the new multi-touch virtual controller Canvas (rendering + hit testing + haptics). |
| android/app/src/main/java/com/doublegate/rustynes/MainActivity.kt | Integrates VirtualController, ORs touch+hardware input, and gates freemium behavior behind PLAY_BUILD. |
| android/app/src/main/AndroidManifest.xml | Adds VIBRATE permission for controller haptics. |
| android/app/build.gradle.kts | Bumps Android app version to 1.8.2 and introduces BuildConfig.PLAY_BUILD=false default. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…llocs Adopt the Gemini/Copilot review findings directly on this branch: - try/finally around the pointer loop so a cancelled gesture (parent intercept, focus loss, disposal) always clears the touch mask — no stuck buttons [HIGH]. - synchronize EmulatorHandle.setTouchMask/onKey so the touch + key masks can't clobber each other under concurrent updates [HIGH]. - drop per-frame allocations on the 60fps draw path: inline the SELECT/START and A/B draw loops (no listOf/Pair), and remember the label Paint instead of allocating it each draw. - use the Float hypot overload in hitTest (no Double round-trip). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…llocs Adopt the Gemini/Copilot findings from the v1.8.2 review (the code now lives in this v1.8.3 branch): - try/finally around the pointer loop so a cancelled gesture (parent intercept, focus loss, disposal) always clears the touch mask — no stuck buttons [HIGH]. - synchronize EmulatorHandle.setTouchMask/onKey so the touch + key masks can't clobber each other under concurrent updates [HIGH]. - drop per-frame allocations on the 60fps draw path: inline the SELECT/START and A/B draw loops (no listOf/Pair), and remember the label Paint instead of allocating it each draw. - use the Float hypot overload in hitTest (no Double round-trip). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.8.2 "Android" — input & the virtual controller
The input increment on the v1.8.0 platform line. Verified end-to-end on a Samsung Galaxy Z Fold 7.
Added
VirtualController.kt): one Canvas draws an NES-001 pad styled per the RustyNES icon (nes_controller()geometry + palette, pressed-state lighting) and collects input via one pointer loop that tracks every pointer by id and recomputes the fullButtonsmask from the whole set — so 3+ simultaneous presses (D-pad + B + A), diagonals, and slide all work. Touch + hardware-gamepad masks are OR'd. The art and hit regions derive from the measured size, so they resize + remap in lockstep across the Z Fold 7 cover/inner displays. Light haptics (systemVibrator,EFFECT_CLICK) on each new press.Changed
make_icon.py): "Rusty" now matches "NES" size (baseline kept, nudged right ~1/8 char, overlapping the cog). Refreshesassets/RustyNES_Icon/, the Android mipmaps, and the README logo — desktop icon updates too.PLAY_BUILDflag (default false): the freemium engages only in the Play AAB; sideload/GitHub/dev builds are full-featured (Play launch held to v1.8.6).Also: refreshed README image assets (+
images/archive/-v1.5 originals). Version bumped 1.8.1 → 1.8.2; the byte-identical core keeps AccuracyCoin 100% (139/139).🤖 Generated with Claude Code