Repository navigation
Conversation
A new android/ app (Kotlin, Hotwire Native Android 1.3.2, AGP 9.5, Gradle 9.8.2, minSdk 28, target 36) wrapping a Rupert instance the same way ios/ does: - Same five tabs, each with its own back stack; forms as modals; pull to refresh. - Server picker in App settings: https only, plain http only for this device and local names; the address must answer like Rupert before the app moves there. - Google sign-in in a Custom Tab with the existing PKCE handoff (rupert://auth/google, POST /native/session); workspace handoff natively. - App lock (BiometricPrompt, screen lock), cover over every screen, no recent-apps snapshot; App settings screen; launcher shortcuts. - Barcode scanning with Google's code scanner (no camera permission). - Top bar follows each page's banner colour, live on appearance changes; dark mode. - Network security config: cleartext off, system CAs only; no backups. One copy of the navigation rules: Gradle copies the iPhone app's path-configuration.json into the app's assets; the server also serves them at /native/android/path-configuration.json. Also on the server: an App Links statement at /.well-known/assetlinks.json from RUPERT_ANDROID_CERT_SHA256, the inventory scanner page talks to the Android bridge, and access logs name the app. CI: .github/workflows/android.yml runs unit/Robolectric tests and lint and keeps the debug APK and the unsigned release bundle. Play Console steps the owner does are in android/PlayStore/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Home starts on the landing page after Google sign-in or a workspace handoff (routing it from onNavigatorReady crashed before the first page existed). - Signing in on a tab starts that tab over, so the signed-in page is its first page. - App lock: leaving for the Google tab, scanner, camera or file picker never locks, however slow the other app is to open; the allowance ends when the app returns. - Network security config listed localhost twice, which Android rejects at start. - Android 9-12: URL decoding without the API 33 overload (lint). - Tab bar and loading screen use Rupert's colours; the activity follows dark mode. - Server address errors clear while typing; friendlier message while Play services is still fetching the barcode scanner. - Drop the deprecated USE_FINGERPRINT permission. - Phone screenshots (1080x2160) from the emulator against the fictional demo shop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bundler-audit on main fails on advisories published today against loofah 2.25.2, nokogiri 1.19.4 and rails-html-sanitizer 1.7.1. Conservative update of only those three gems to loofah 2.25.3, nokogiri 1.19.7 and rails-html-sanitizer 1.7.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test read /proc/loadavg and /proc/meminfo directly, so it failed on a Mac. HostVitals now reads /proc through one small method that the test stubs with fixed contents, checking the exact CPU and memory percentages instead of only their types. Disk and database size still come from the real host. The real /proc read still runs where /proc exists (Linux CI). A new test pins that an unreadable reading is nil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under a C/POSIX locale (a shell without LANG, as on some Macs) Ruby returns ENV values as binary, so a label such as "SER8 · Incus" did not equal the UTF-8 string and the platform overview test failed. The label is now read as UTF-8, and a value that is not valid UTF-8 shows as "Not configured" instead of raising. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PosPaymentReconciliationJob takes one machine-wide flock and skips its sweep when the lock is held. Parallel test workers shared that file, so when two PosCardPaymentTest cases ran the job at the same moment one silently did nothing. That intermittently failed the overlap test (line 72) and the rotation test (line 56) on main and on several PRs. The lock path is now one small method, and the tests point it at a temporary file per test. Holding the real lock from another process reproduces both failures on the old code, and they pass on the new. Production still uses tmp/locks/pos-payment-reconciliation.lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps this branch from conflicting with the support-address change in #616. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Adds Rupert for Android (
android/), the Android twin of the iPhone app: a Hotwire Native Android 1.3.2 shell (Kotlin, AGP 9.5, Gradle 9.8.2, minSdk 28, target 36,com.rupertpos.rupert) around a Rupert instance, plus the small server changes it needs. Nothing is uploaded to Google; no signing material is in the repo.Why Hotwire Native (and not
mobile/)mobile/is the Expo app for staff timesheets only (/employeeAPI); it is maintained but narrow. The full Rupert app is the website, andios/already wraps it with Hotwire Native. Doing the same on Android reuses everything the server already does for the iPhone app (hotwire_native_app?matchesHotwire Native Android, hidden web chrome, hidden billing/sign-up, the Google PKCE handoff, the workspace handoff), shares the navigation rules, and means new pages need no app release.Parity with iOS
pos.rupertpos.com; App settings → Rupert server picks a self-hosted Rupert. The address is checked twice: form (InstanceAddress: https only, no paths or credentials; http only for this device and.local/.lan/.internal/.home.arpa) and that it serves Rupert's navigation rules. Workspace instances are remembered.rupert://auth/google→POST /native/session, cookie copied into the web views). Sign-upintentis carried.rupert://workspace?…caught by the router; the app posts the handoff to<origin>/instance/sessionwith the control-plane Origin, keeps only that instance's cookie, moves every tab./login), Team Chat, Ask Rupert, Notifications, Search; own back stacks; tabs left behind by a sign-in/out start over.rupert:scannedevent.--color-banner, live when the appearance changes.rupert://auth/google; App Links forpos.rupertpos.comon chosen paths (sign-in paths excluded), verified once the owner sets the certificate (below).ios/Sources/Rupert/Resources/path-configuration.jsoninto the app at build time; server serves the same rules at/native/android/path-configuration.json;native_app_test.rbasserts they match.Security
HTTPS only (
network_security_config.xml: cleartext off, system CAs only; local exceptions mirrorInstanceAddress, enforced by a test). No runtime permissions (INTERNET, plus install-timeUSE_BIOMETRICandACCESS_NETWORK_STATEfrom libraries). Page bridges are WebView message listeners limited to Rupert origins and main frames, never payment iframes. No backups, no WebView metrics, file/content access off, debugging only in debug builds..gitignorerefuses keystores and Play service-account files. Data safety answers are inandroid/PlayStore/data-safety.md.Server changes (
web/)GET /native/android/path-configuration.json(same rules as iOS).GET /.well-known/assetlinks.json(AndroidAssetLinksController): empty list untilRUPERT_ANDROID_CERT_SHA256is set.inventory_count_scanner_controller.js: useswindow.rupertScannerwhen the iOS handler is absent.native_app_test.rb(18 runs green locally).CI
.github/workflows/android.yml(pinned actions): unit + Robolectric tests, lint, debug APK, unsigned release AAB as artifacts. Runs only onandroid/**and the shared path-configuration file.Tested
./gradlew testDebugUnitTest: 33 tests pass (address rules, PKCE vs the server'schallenge_for, rupert:// parsing, network config ⇔ address rules, banner parsing, HTTP flows against MockWebServer, Robolectric screens).lintDebugclean (3 minor warnings),assembleDebug,bundleRelease(unsigned, 7.1 MB), and the R8 release build runs on the emulator.connectedDebugAndroidTestsmoke test passes on an Android 16 emulator.http://10.0.2.2:3000, the fictional review demo shop): server picker (rejectshttp://192.168.x, accepts local), email sign-in, all tabs, scan page bridges, live dark appearance, app dark mode, app lock (on/off, cover, recents, unlock), and Google sign-in end to end (Custom Tab withnative_challengeandintent; a code minted locally for the app's challenge redeemed → session → Home opened on Rupert's landing page; a code for another challenge was refused with 422).Left for the owner
See
android/PlayStore/checklist.md: Play developer account (Organization avoids the 12-tester closed-test rule), upload key + signed bundle, Play App Signing, internal testing,RUPERT_ANDROID_CERT_SHA256for App Links, listing, Data safety (check Google's ML Kit disclosure for the scanner), content rating, target audience, review login viarupert:app_review_demo.Known follow-ups
document.title(seen as "#shop-floor · Team chat" after visiting from chat); a web-side title issue that also affects iOS.🤖 Generated with Claude Code