A phone-hosted file-browser web server for Android. Run it on your phone; any device on the same Wi-Fi opens the URL in a browser to view and upload files on the phone's storage. Inspired by TianYan, Kotlin-native with a bundled minimal frontend.
- Web server on the phone — NanoHTTPD inside a
dataSyncforeground service. The URL is shown in the app and the ongoing notification. - Auto-detects the device's storage root across Android variants (
/sdcard,Environment.getExternalStorageDirectory(),/storage/emulated/0,/storage/self/primary, plus external volume roots). Exposed viaGET /api/roots. - Storage scope:
MANAGE_EXTERNAL_STORAGE(full primary external access). Navigation clamped at the discovered root. - Directory listings use
Files.readAttributes(BasicFileAttributes)— onestat()per file (vs. 4× with bareFile). ~4× faster on FUSE storage. - Bundled frontend (vanilla JS, no build step): path bar, dir listing with file-type icons, preview pane (image / video with
Range/ audio / pretty-printed JSON / text / binary fallback as download link). - Row actions (⋮, right-click, or long-press): Preview · Download · Rename · Copy path · Show full path.
- Large multi-file uploads: files posted one at a time via XHR (server temp peaks at one file size, not Σ all); temp dir lives in external cache (
Android/data/com.shock.mobileeye/cache/uploads, tens of GB available); temp→destination usesFiles.move()when same-volume.
| Method | Path | Notes |
|---|---|---|
| GET | /api/health |
{"status":"ok"} |
| GET | /api/roots |
{"default":"/sdcard","candidates":[…]} |
| GET | /api/directory?path=… |
{path, parent, items:[{name,is_dir,size,mtime}]} |
| GET | /api/file?path=…&download=1 |
raw bytes, supports Range; download=1 forces attachment |
| GET | /api/video?path=… |
alias of /api/file (Range) |
| POST | /api/upload |
multipart: dir, files[], paths[], overwrite |
| POST | /api/rename |
JSON: {path, new_name} (refuses traversal / separators) |
Static assets served from assets/web/; SPA fallback to index.html.
Kotlin · AndroidX · Material 3 · NanoHTTPD 2.3.1 · OkHttp 4.12 · ViewBinding · minSdk 26 · targetSdk 34.
export JAVA_HOME=/path/to/jdk-17
export ANDROID_SDK_ROOT=/path/to/android-sdk
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkFirst run: grant All files access when prompted (Settings → mobileEye → toggle on).
app/src/main/java/com/shock/mobileeye/
├── MainActivity.kt server control UI: URL display, start/stop, port, permission request
├── WebServerService.kt foreground service; notification with URL; LAN IP detection
└── WebServer.kt NanoHTTPD subclass, all route handlers, multipart upload, Range, rename
app/src/main/assets/web/
├── index.html single page
├── app.js directory browser, viewer, sequential uploader, row-action menu
└── app.css styling
No auth. Anyone on the same Wi-Fi can read and upload. Stop the server on untrusted networks, or add a token/PIN before exposing more broadly.
MIT.