3.6 KiB
Android → PC File Drop
Minimal Kotlin Android app that picks files/images and uploads them to a folder on a PC over the local network.
Architecture
Two pieces:
- PC receiver — tiny HTTP server that accepts multipart uploads and writes files into a chosen folder.
- Android app — one screen, one button: pick files → POST them to the PC.
No cloud, no accounts, no discovery protocol. You type (or QR-scan) the PC's LAN IP once and save it.
PC Receiver
Language: Go (single binary). Go for "double-click and forget."
Behavior:
- Listen on
0.0.0.0:8787. POST /uploadacceptsmultipart/form-data, saves each part toDROP_DIRwith its original filename.- Collision policy: append
(1),(2), etc. - Optional shared-secret header (
X-Auth: <token>) so randos on the same Wi-Fi can't dump files on you. GET /returns "ok" for a health check.- Log each received file: name, size, source IP.
it is seen on the local panel so that user locally can download
Config:
DROP_DIR— where files land (default~/Drops).AUTH_TOKEN— shared secret (generated on first run, printed + saved to config).PORT— default 8787.
Firewall: open the port on the PC's LAN profile only.
Android App
Stack: Kotlin, Jetpack Compose, single Activity, minSdk 26.
Screens: one.
UI:
- Text field: PC address (e.g.
192.168.1.42:8787), persisted inDataStore. - Text field: auth token, persisted.
- Big button: "Pick files".
- Below: list of currently-selected files with progress bars during upload.
- Snackbar for success/failure per file.
Flow:
- Tap button →
ActivityResultContracts.OpenMultipleDocuments()(handles images, video, any file; no storage permission needed on modern Android). - For each returned
Uri: stream it as a multipart part tohttp://<address>/uploadwith the auth header. - Show per-file progress; on 200, mark done; on error, show retry.
Networking: OkHttp with a MultipartBody, streaming from contentResolver.openInputStream(uri) so big videos don't OOM.
Manifest:
INTERNETpermission.usesCleartextTraffic="true"scoped to local IP ranges vianetwork_security_config.xml(HTTP is fine on LAN; adding TLS is a stretch goal).
Share target (stretch): register as a share target so you can hit "Share → Drop to PC" from the Photos app.
Protocol
POST /upload HTTP/1.1
Host: 192.168.1.42:8787
X-Auth: <token>
Content-Type: multipart/form-data; boundary=...
--boundary
Content-Disposition: form-data; name="file"; filename="IMG_1234.jpg"
Content-Type: image/jpeg
<bytes>
--boundary--
Response: 200 OK with JSON {"saved": "IMG_1234.jpg"} or 4xx/5xx with error text.
Milestones
- PC receiver in ~60 lines; test with
curl -F file=@foo.jpg http://localhost:8787/upload -H "X-Auth: ...". - Android app scaffold: Compose UI, DataStore for settings, file picker returning URIs.
- Wire up OkHttp multipart upload from a URI stream.
- Progress + error UI.
- Package: PC receiver as a systemd user service (Linux) or Task Scheduler entry (Windows) so it autostarts. Android side sideload the APK.
Stretch
- QR code on PC receiver's
GET /page encodingaddress + tokenso first-time setup is scan-once. - mDNS advertisement (
_filedrop._tcp.local) so the app can discover the PC without typing IPs. - TLS with a self-signed cert pinned in the app.
- Share-target intent filter.
- Resume interrupted uploads (
Content-Range).
Non-goals
- PC → Android direction (use MTP or a second app instance later).
- Internet / NAT traversal.
- Multiple PCs at once (one address at a time is fine).