Skip to content

makeing it direct wifi able and removed gliches from core and as well added a apk builder - #4

Merged
Chintanpatel24 merged 4 commits into
mainfrom
qr-apk
Aug 2, 2026
Merged

makeing it direct wifi able and removed gliches from core and as well added a apk builder #4
Chintanpatel24 merged 4 commits into
mainfrom
qr-apk

Conversation

@Chintanpatel24

Copy link
Copy Markdown
Owner

Fiver Wireless Companion App - Implementation Complete

What Was Built

A complete wireless screen mirroring system that works without USB debugging. When a phone scans a QR code, it downloads a companion Android app that captures the screen and streams it live to the laptop over WiFi.

New Files Created

File Purpose
companion/__init__.py Package marker
companion/AndroidManifest.xml Android app manifest with MediaProjection permissions
companion/MainActivity.java Dark-themed UI, requests screen capture permission
companion/ScreenCaptureService.java Foreground service: captures screen → JPEG → HTTP POST to laptop
apk_builder.py Auto-builds APK: finds/downloads SDK → javac → d8 → aapt2 → sign

Modified Files

File Changes
web_mirror.py New /download/companion.apk endpoint, raw JPEG frame support, APK download page
tui.py Companion app fallback when ADB fails: build APK → serve → show QR
pyproject.toml Include companion source files in package data

Complete Flow

User runs: fiver --setup-wifi
    │
    ▼
TUI scans WiFi → picks device → tries ADB connect
    │
    ▼  (ADB fails — no USB debugging)
APK Builder activates:
    ├── Checks for Android SDK tools
    ├── Auto-downloads SDK if missing (~1 min first time)
    ├── Embeds laptop IP into Java source
    ├── javac → d8 → aapt2 → apksigner
    └── Caches built APK
    │
    ▼
Web server starts → Cloudflare tunnel → QR code shown
    │
    ▼  (Phone scans QR)
Phone opens page → "DOWNLOAD & INSTALL" button
    │
    ▼  (User installs APK)
APK opens → "START SCREEN SHARE" button
    │
    ▼  (User taps "Start Now" on permission dialog)
ScreenCaptureService starts:
    ├── MediaProjection captures screen at 720p
    ├── JPEG frames at ~20 FPS, quality 60
    └── HTTP POST raw JPEG to laptop:8080/api/frame
    │
    ▼
Desktop browser shows live MJPEG stream at /desktop

Required Dependencies

Before the APK can be built, you need JDK installed:

# Arch / CachyOS
sudo pacman -S jdk-openjdk

# Debian / Ubuntu
sudo apt install default-jdk

# Fedora
sudo dnf install java-latest-openjdk-devel

Important

The Android SDK build-tools and platform will be auto-downloaded on first use to ~/.fiver/sdk/. No manual Android Studio setup needed.

Key Design Decisions

  • View-only: Laptop can see the phone screen but not control it (avoids Accessibility Service complexity)
  • JPEG over HTTP: Simple, universal, ~15-25 FPS at 720p
  • Auto SDK install: Downloads commandlinetools from Google, then uses sdkmanager to install build-tools;34.0.0 and platforms;android-34
  • APK caching: Built APKs are cached at ~/.fiver/apk/ keyed by server URL hash
  • No USB debugging required: Uses Android's MediaProjection API which only needs user consent

Copilot AI review requested due to automatic review settings August 2, 2026 15:02
@Chintanpatel24
Chintanpatel24 merged commit 376a771 into main Aug 2, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Wi‑Fi companion-app workflow for screen mirroring when ADB over Wi‑Fi is unavailable, including an auto-APK build pipeline and an HTTP endpoint to download the companion APK and accept raw JPEG frames.

Changes:

  • Added an Android companion app (Activity + foreground MediaProjection service) that captures the screen and POSTs JPEG frames to the desktop server.
  • Added APKBuilder to auto-download/install required Android SDK components (when missing), build/sign the APK, and cache it per server URL.
  • Updated the TUI + web mirror server to serve the companion APK and accept image/jpeg frame uploads.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
src/fiver/web_mirror.py Phone landing page now offers companion APK download; adds /download/companion.apk; accepts raw image/jpeg frames.
src/fiver/tui.py Companion-mode fallback: builds APK, serves it, tunnels via Cloudflare, and waits for streaming.
src/fiver/apk_builder.py New APK build/sign/cache implementation with SDK auto-install behavior.
src/fiver/companion/AndroidManifest.xml New manifest for the companion app (permissions + service/activity registration).
src/fiver/companion/MainActivity.java New UI to request MediaProjection permission and start/stop capture service.
src/fiver/companion/ScreenCaptureService.java New foreground service that captures frames and POSTs JPEG to /api/frame.
src/fiver/companion/init.py Package marker for bundling companion sources.
src/fiver/banner.py Updates ASCII banner art.
pyproject.toml Packages companion .java/.xml sources as package data.
patch.py Adds a local patching script (currently hard-coded to an absolute path).
build/lib/fiver/banner.py Updates banner art in a build artifact copy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/fiver/web_mirror.py
Comment on lines +256 to +260
with open(MirrorHandler.apk_path, "rb") as f:
apk_data = f.read()
self.send_header("Content-Length", str(len(apk_data)))
self.end_headers()
self.wfile.write(apk_data)
Comment thread src/fiver/web_mirror.py
Comment on lines +236 to +239
host = self.headers.get("Host", "localhost")
proto = "https" if isinstance(self.connection, ssl.SSLSocket) else "http"
html = PHONE_HTML.replace("{{LOCAL_SERVER_URL}}", f"{proto}://{host}")
self.wfile.write(html.encode("utf-8"))
Comment thread src/fiver/tui.py
Comment on lines 520 to 522
local_ip, _ = _local_subnet()
local_ip = local_ip or "127.0.0.1"

Comment thread src/fiver/tui.py
Comment on lines 527 to 529
self.web_server = WebMirrorServer(port=8080)
self.web_server.start()
server_addr = self.web_server.start()

Comment on lines +12 to +14
<application
android:label="Fiver Mirror"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar">
Comment thread src/fiver/apk_builder.py
Comment on lines +156 to +162
zip_path = _SDK_DIR / "cmdline-tools.zip"
try:
_run(["curl", "-fsSL", "-o", str(zip_path), _CMDLINE_TOOLS_URL])
except RuntimeError:
# Fallback to wget
_run(["wget", "-q", "-O", str(zip_path), _CMDLINE_TOOLS_URL])

Comment thread src/fiver/apk_builder.py
Comment on lines +214 to +250
@staticmethod
def _cache_key(server_url: str) -> str:
return hashlib.sha256(server_url.encode()).hexdigest()[:16]

def get_cached_apk(self, server_url: str) -> Optional[str]:
"""Return path to cached APK if it exists for this server_url."""
key = self._cache_key(server_url)
path = _APK_CACHE_DIR / f"companion-{key}.apk"
return str(path) if path.is_file() else None

# ── Build ─────────────────────────────────────────────────

def build_apk(self, server_url: str) -> str:
"""Build the companion APK with *server_url* embedded.

Returns the absolute path to the signed APK.
Raises ``RuntimeError`` on failure.
"""
# 1. Cache check
cached = self.get_cached_apk(server_url)
if cached:
log.info("Using cached APK: %s", cached)
return cached

# 2. Tool check — auto-install if needed
missing = self._missing_tools()
if missing:
log.info("Missing SDK tools: %s — attempting auto-install", missing)
self.install_sdk()
missing = self._missing_tools()
if missing:
raise RuntimeError(
f"Required tools not found: {', '.join(missing)}.\n"
"Please install the Android SDK build-tools and platform.\n"
" Arch/CachyOS: yay -S android-sdk-build-tools android-platform\n"
" Or run: fiver --doctor"
)
Comment thread patch.py
Comment on lines +1 to +12
import re
import os

def patch_web_mirror():
path = "/home/cachy/github-p/fiver/src/fiver/web_mirror.py"
with open(path, "r") as f:
content = f.read()

# 1. PHONE_HTML
start_str = '# HTML template for the phone (Matte Black Theme, Instant Tap Permission)'
end_str = '"""\n\n# HTML template for the Desktop viewer'

Comment thread build/lib/fiver/banner.py
Comment on lines 1 to 16
"""ASCII branding for the fiver CLI."""

from __future__ import annotations

from . import __version__

ART = r"""
░▒▓████████▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓███████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒▒▓█▓▒░░▒▓██████▓▒░ ░▒▓███████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░
ART = r"""
███████╗██╗██╗ ██╗███████╗██████╗
██╔════╝██║██║ ██║██╔════╝██╔══██╗
█████╗ ██║██║ ██║█████╗ ██████╔╝
██╔══╝ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗
██║ ██║ ╚████╔╝ ███████╗██║ ██║
╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
"""

TAGLINE = " android desk control | local server | your device"
Comment thread src/fiver/tui.py
Comment on lines +572 to 575
# Poll until phone starts streaming or user exits
while not MirrorHandler.accepted and not MirrorHandler.declined:
time.sleep(0.5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants