"Turn a website into a native Android app (APK) with V-Run's Publish → Android app, and use what a web page can't: scanning nearby Wi-Fi networks for indoor positioning — which floor or room someone is on — through window.VRunWifi.scan(). Use when the user wants an Android app, an APK, to install the app on their phone, Wi-Fi scanning, indoor or floor detection, or anything a browser page is not allowed to do on a phone."
Install
npx skillscat add v-groups-foundations/freetoken/android-app Install via the SkillsCat registry.
Android app (Capacitor) and native Wi-Fi scanning
A web page on a phone can't scan Wi-Fi networks or read signal strengths. V-Run wraps the project's site
in a native Android app (Capacitor 8) that can, and builds an APK.
First: the tools (always, before building)
- Call
setup_androidfirst whenever the user wants a phone app. It checks Android Studio, the
Android SDK and Java 21. If something is missing it asks the user before installing anything
(Android Studio with winget / Homebrew, ~1.5 GB; Java 21 into V-Run's own folder, ~190 MB) and opens
Android Studio so its setup downloads the SDK. If they decline or it can't install here, give them the
link it returns (https://developer.android.com/studio) — never install tools some other way. - Android Studio's own Java (25) is too new for the build tools (Gradle 8 runs on Java 21–24): that's
why V-Run adds its own Java 21. Don't change Gradle or Java versions inandroid/to work around it. - After the user finishes Android Studio's first-run setup, call
setup_androidagain, thenbuild_android_app(asks for approval) to get the APK.
How the user gets the app
- Preview → Publish → Android app: Set up… (the same tools check and install, with a confirmation),
Build APK, or Prepare only (makes theandroid/project without those tools). - Install on phone (USB) installs it with adb (USB debugging on). Or copy the APK to the phone.
- Rebuild after every change to the site: the app carries its own copy of the site.
- Don't edit
android/by hand unless needed; V-Run adds the Wi-Fi plugin (WifiScanPlugin.java, its
registration inMainActivity.java, the location/Wi-Fi permissions) every time it prepares.
Using Wi-Fi in the page
V-Run adds vrun-native.js to the app's copy of the site and loads it before the page's scripts:
if (window.VRunWifi?.available()) {
const networks = await VRunWifi.scan(); // [{ bssid, ssid, level /* dBm, e.g. -55 */, frequency /* MHz */ }]
}- In a normal browser
available()is false andscan()rejects: always keep a fallback
(a floor picker, GPS, or a QR code per floor) so the site still works on the web. - The first
scan()asks for the location permission (Android requires it to see Wi-Fi networks). - Android throttles real scans to about 4 per 2 minutes per app; between them
scan()returns the
latest results (fresh: false). Scan every 30 s or so, not in a tight loop.
Floor / room detection with Wi-Fi fingerprints (the reliable way)
GPS can't tell floors apart indoors; Wi-Fi signal patterns can.
- Calibrate: on each floor, scan a few times and save a fingerprint: BSSID → average dBm.
Store fingerprints in the app (localStorage) or on the server so everyone shares them. - Locate: scan, then compare against each floor's fingerprint — e.g. for every BSSID seen in both,
sum (a − b)², count a BSSID missing on one side as −100 dBm, and pick the floor with the smallest
distance (k-nearest-neighbours over several fingerprints per floor is more robust). - Smooth: use the floor that wins most of the last 3 scans, so a person doesn't flicker between floors.
- Show the confidence, and let the user correct the floor (and save that as a new fingerprint).
Sharing location live
Live sharing between phones needs a server (WebSocket or a hosted database): a static website host
can't do it. Keep the server's address configurable, and say so plainly to the user.