Files

7.3 KiB

DroidScope — Development Guide

Prerequisites

Tool Version Install
Go 1.23+ https://go.dev/dl
Node.js 20+ https://nodejs.org
Wails CLI v2 go install github.com/wailsapp/wails/v2/cmd/wails@latest
Git any

macOS only: Wails requires Xcode Command Line Tools — xcode-select --install

Linux only: Wails requires libgtk-3-dev libwebkit2gtk-4.0-dev (Debian/Ubuntu) or equivalent.

Windows only: Wails requires WebView2 (ships with Windows 11, otherwise install from Microsoft).


First-time Setup

# 1. Install frontend dependencies
cd frontend-react
npm install

# 2. (Optional) Download bundled ADB binaries for distribution builds
# Skip this for local dev — the app falls back to your system ADB
bash scripts/download-adb.sh

# 3. Make sure your system ADB is accessible for local dev
# Either via ANDROID_HOME or PATH:
export ANDROID_HOME=~/Library/Android/sdk   # macOS example
# or: ensure `adb` is on your PATH

Running in Development

bash scripts/dev.sh

What dev.sh does, in order:

Step Command What it checks
1 go build ./... in backend-go/ Backend packages compile without errors
2 go build ./... in desktop/ Desktop Go code (app.go, main.go, embeds) compiles
3 wails generate module in desktop/ Regenerates frontend-react/src/wailsjs/ from current app.go bindings
4 npm install + tsc -b --noEmit in frontend-react/ Installs any new deps, type-checks TypeScript
wails dev in desktop/ Starts the desktop app with hot reload

If any step fails the script exits immediately (due to set -e) — fix the error and re-run.

What happens once running:

  • Wails compiles the Go backend and opens a native desktop window
  • Wails starts the Vite dev server (npm run dev in frontend-react/)
  • React/TS changes hot-reload instantly without restarting
  • Go changes (in app.go, backend-go/) require re-running dev.sh

When to re-run dev.sh vs just saving a file:

  • Edited only .tsx / .ts / .css → Vite hot-reloads automatically, no restart needed
  • Edited app.go (added/changed a bound method) → re-run dev.sh to rebuild Go + regenerate bindings
  • Edited anything under backend-go/ → re-run dev.sh to rebuild

Frontend only (browser preview, no Wails bindings):

cd frontend-react
npm run dev
# → http://localhost:5173

Wails bindings (ListDevices, GetDeviceInfo, etc.) are no-ops in plain browser mode — they only work inside the Wails desktop window.


Building for Production

bash scripts/build.sh

Or manually:

# Step 1 — build frontend (outputs to desktop/frontend/dist/)
cd frontend-react
npm run build

# Step 2 — build desktop binary (embeds frontend dist + ADB binaries)
cd desktop
export PATH="$PATH:$(go env GOPATH)/bin"
wails build

Output: desktop/build/bin/DroidScope (macOS/Linux) or desktop/build/bin/DroidScope.exe (Windows)


Backend only

cd backend-go
go build ./...

Frontend only (static bundle)

cd frontend-react
npm run build
# Output: desktop/frontend/dist/

Android SDK

cd android-sdk
./gradlew :sdk:assembleDebug     # debug AAR
./gradlew :sdk:assembleRelease   # release AAR
# Output: android-sdk/sdk/build/outputs/aar/

Type Checking (no build)

# TypeScript
cd frontend-react
npx tsc -b --noEmit

# Go
cd backend-go && go build ./...
cd desktop && go build ./...

Regenerating Wails JS Bindings

Run this after adding or changing any exported method on App in desktop/app.go:

cd desktop
export PATH="$PATH:$(go env GOPATH)/bin"
wails generate module
# Output: frontend-react/src/wailsjs/

Project Structure

DroidScope/
├── desktop/                    ← Wails app (Go entry point)
│   ├── app.go                  ← Wails-bound methods (called from frontend)
│   ├── main.go                 ← App bootstrap
│   ├── wails.json              ← Wails config (frontend dir, dev server)
│   └── internal/adbembed/      ← ADB binary embedding + runtime extraction
│
├── backend-go/                 ← Go backend packages
│   ├── adb/                    ← ADB command wrapper
│   ├── device/                 ← Device manager + DeviceInfo fetcher
│   ├── logcat/                 ← (stub) Logcat streaming
│   ├── network/                ← (stub) Network inspector
│   ├── storage/                ← (stub) SharedPrefs, SQLite, files
│   ├── runtime/                ← (stub) Runtime config override
│   ├── session/                ← (stub) Session management
│   └── protocol/               ← Shared event types (Go structs)
│
├── frontend-react/             ← Vite + React + TypeScript
│   ├── src/
│   │   ├── wailsjs/            ← Auto-generated Wails bindings (do not edit)
│   │   ├── features/           ← Feature modules (devices, logcat, network…)
│   │   ├── components/         ← Shared UI components (layout, shadcn/ui)
│   │   ├── hooks/              ← Shared hooks (useActiveDevice)
│   │   ├── store/              ← Zustand global state
│   │   └── types/              ← Shared TypeScript types (protocol)
│   └── vite.config.ts          ← Build outputs to ../desktop/frontend/dist/
│
├── android-sdk/                ← Kotlin Android SDK (Gradle)
│   └── sdk/src/main/kotlin/dev/achmad/droidscope/
│
├── scripts/
│   ├── dev.sh                  ← Start full dev environment
│   ├── build.sh                ← Production build
│   └── download-adb.sh         ← Download ADB binaries for bundling
│
├── go.work                     ← Go workspace (links desktop + backend-go)
├── README.md                   ← Project overview and feature roadmap
└── docs/
    ├── DEVELOPMENT.md          ← This file
    └── TODO.md                 ← Implementation status

Key Conventions

Adding a new Go method callable from the frontend

  1. Add the method to desktop/app.go (must be exported, receiver *App)
  2. Run wails generate module from desktop/
  3. Import from @/wailsjs/go/main/App in React

Adding a new backend-go package

  1. Create the package under backend-go/
  2. The go.work workspace + replace directive in desktop/go.mod handle local resolution — no publishing needed

Active device scoping (all features)

  • Import useActiveDevice from @/hooks/use-active-device
  • Check isMonitoring before starting streams or polls
  • Store per-device data in deviceCache via patchDeviceCache(deviceId, data) — never cleared on device switch
  • Show <NoDeviceSelected /> when !isMonitoring

ADB Binary Bundling

For local development the app resolves ADB in this order:

  1. Embedded binary in desktop/internal/adbembed/bin/ (empty until download-adb.sh is run)
  2. $ANDROID_HOME/platform-tools/adb
  3. adb on $PATH

For distribution builds run scripts/download-adb.sh once before wails build to embed ADB for all platforms.