The dumb loop
I'm a solo dev building mobile apps with AI coding tools (Claude Code, mostly). My feedback loop looked like this:
- See a UI bug on my phone
- Screenshot it
- Send it to myself on WhatsApp
- Open WhatsApp Web on my PC
- Download the image
- Drag it into the AI chat
- Repeat 30 times a day
Every mobile dev I asked did some version of this — WhatsApp-to-self, Telegram saved messages, email drafts, USB cable and file explorer. It's a 45-second detour for something that should be instant.
So I spent three weeks building DevShot: tap a floating bubble on your Android phone, and the screenshot lands on your Windows PC's clipboard over local WiFi. Ctrl+V into anything. No cloud, no account.
This post is about the technical decisions and the traps I fell into.
Architecture
Phone (Kotlin) ──WebSocket/TLS──▶ Desktop (Python/PyQt6) ──▶ Clipboard
│
└──▶ ~/devshot/mobile-latest.png
Three decisions I locked in early:
Native Kotlin, not Flutter/React Native. Screen capture on Android means MediaProjection, and MediaProjection is painful enough without a cross-platform abstraction layer on top of it. The permission lifecycle (user grants → foreground service → projection can be revoked at any time) needs precise control.
Python + PyQt6 for the desktop, not Electron. The desktop app is a system tray icon, a pairing window with a QR code, and a WebSocket server. Shipping a whole Chromium runtime for that felt absurd. PyInstaller gives me a single .exe.
Local WiFi only, no relay server. This was the big one. Every "send to PC" app routes through someone's cloud. I wanted the pitch to be: your screenshots physically cannot leave your network, because there is no server. Privacy as architecture, not as a policy document.
The parts that fought back
1. MediaProjection is a permission minefield
Android (rightly) treats screen capture as radioactive. You need:
- A one-time user grant via
createScreenCaptureIntent() - A foreground service with type
mediaProjectionrunning before you use the token - Handling for the projection being killed at any moment (battery savers on Xiaomi/Samsung do this enthusiastically)
The floating bubble itself is a WindowManager overlay from a foreground service — a second permission (SYSTEM_ALERT_WINDOW) with its own settings page dance.
2. Pairing UX vs. TLS on a LAN
I wanted encryption, but you can't get a CA-signed certificate for 192.168.1.5. Self-signed certs mean man-in-the-middle risk on the first connection.
The fix: the desktop generates a self-signed cert on first launch and shows a QR code that embeds the certificate fingerprint along with the IP and a pairing token. The phone scans it and pins that exact fingerprint for all future connections. The QR code becomes the trust anchor — MITM would require compromising the QR code, which is displayed on the very screen you're standing in front of.
3. The Windows Firewall silent killer
This one cost me a full debugging session after release. If a user clicks "Cancel" on the Windows Firewall popup (instead of "Allow"), Windows creates a permanent Block rule for your app. Not "no rule" — an active Block, and Block beats any Allow rule you add later. The phone can never connect, and nothing tells you why.
The fix was making the installer register the firewall rule itself during setup:
netsh advfirewall firewall add rule name="DevShot" dir=in action=allow program="...\DevShot-GUI.exe"
(Inno Setup, elevated via ShellExec runas since the installer itself runs unprivileged.) If you ship anything with a listening socket on Windows, do this on day one.
4. The filesystem is an API
Besides the clipboard, every screenshot is written to a predictable path:
~/devshot/
├── mobile-latest.png ← always the newest phone screenshot
├── mobile-latest.json ← metadata (device, resolution, timestamp)
└── mobile-history/ ← one file per capture
This turned out to be the feature AI-tool users care about most. CLI agents can just read ~/devshot/mobile-latest.png by path — no integration, no plugin, no MCP server. A stable file path is the most universal API there is.
What I deliberately didn't build
- iOS/Mac support — v2, maybe. Solo dev, fixed scope.
- Cross-network / internet mode — would need a relay server, which breaks the entire privacy model.
- Accounts — license keys via LemonSqueezy for the Pro tier, validated on-device. No user database anywhere.
- Analytics SDK — there's no telemetry in the app at all.
Cutting scope is what let me ship in three weeks instead of never.
The result
Free tier does 15 screenshots/day; Pro ($9/mo) is unlimited with multi-device. Windows + Android today.
Honest caveats: the installer is unsigned (code-signing certs are $200+/yr — SmartScreen will warn you), and the site is still on a vercel.app subdomain. Solo-dev life.
Site: https://devshot-app.vercel.app
I'd genuinely love feedback from this community — especially on the QR-pinned-TLS approach, and whether the free/Pro split feels fair. Ask me anything about MediaProjection scars in the comments.
Top comments (0)