d6956395c8
Rename the whole project from CommGate to gsmnode and re-skin every surface onto the new signal-green + ink design system (Space Grotesk / IBM Plex Sans / JetBrains Mono, flat surfaces, two-arrow routing mark, lowercase gsm+node wordmark). - Web App + API panel: rewrite token layer, gn-* utilities, data-gsm-theme, new logo/favicon assets; both builds verified. - Phone App (Flutter): green theme, new mark/wordmark widgets, adaptive launcher icons; rename Android applicationId/package to app.gsmnode.phone; bump AGP to 8.6.0 and google_fonts to 8.1.0; debug APK build verified. - API Server (Go): panel routes, User-Agent, health service id, branding. - Home Assistant Plugin: rename integration domain sms_gateway to gsmnode (folder, DOMAIN, services, entity ids, classes); py_compile verified. - Update all READMEs; add .gitignore (excludes Design/, build artifacts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
125 lines
5.5 KiB
Markdown
125 lines
5.5 KiB
Markdown
# gsmnode — Phone App (Flutter / Android)
|
|
|
|
Turns an Android phone into the SMS gateway endpoint. It registers with the API
|
|
Server, polls for pending outbound messages, sends them over the radio, reports
|
|
delivery state, and forwards incoming SMS to the server's inbox.
|
|
|
|
```
|
|
API Server ──(pending messages)──► Phone App ──► SmsManager (send)
|
|
▲ │
|
|
└──(status reports / inbox)──────────┘ ◄── BroadcastReceiver (incoming)
|
|
```
|
|
|
|
> The Phone App talks **only** to the API Server (never to PocketBase directly),
|
|
> using the device token issued at registration.
|
|
|
|
## Status: source scaffold
|
|
|
|
This folder contains the complete Dart app and the native Android (Kotlin) SMS
|
|
bridge, but **not** the generated Gradle/platform scaffolding. You generate that
|
|
once with `flutter create` after installing the toolchain (below).
|
|
|
|
## Prerequisites
|
|
|
|
1. **Flutter SDK** (stable) — https://docs.flutter.dev/get-started/install/windows
|
|
2. **JDK 17** (this machine currently has only JDK 8 — Android Gradle needs 17+)
|
|
3. **Android SDK** (via Android Studio or `flutter doctor --android-licenses`)
|
|
4. A **physical Android phone** with a SIM (the emulator can't send real SMS)
|
|
|
|
Verify with `flutter doctor` — resolve anything it flags before continuing.
|
|
|
|
## Generate platform scaffolding & wire in the native code
|
|
|
|
From this `Phone App/` folder:
|
|
|
|
```powershell
|
|
# 1. Generate the android/ Gradle project (keeps lib/ and pubspec.yaml)
|
|
flutter create . --org app.smsgateway --project-name sms_gateway_phone --platforms=android
|
|
|
|
# 2. Overlay the SMS-enabled manifest + Kotlin (overwrites the generated stubs)
|
|
Copy-Item -Recurse -Force android_overlay/* android/
|
|
|
|
# 3. Fetch packages
|
|
flutter pub get
|
|
```
|
|
|
|
`android_overlay/` mirrors the real `android/` paths, so step 2 drops:
|
|
- `app/src/main/AndroidManifest.xml` — SMS permissions + the `SmsReceiver`
|
|
- `app/src/main/kotlin/app/smsgateway/sms_gateway_phone/MainActivity.kt` — send bridge
|
|
- `app/src/main/kotlin/app/smsgateway/sms_gateway_phone/SmsReceiver.kt` — incoming bridge
|
|
|
|
If Gradle complains about SDK levels, set `minSdkVersion 23` (or higher) in
|
|
`android/app/build.gradle`.
|
|
|
|
## Run
|
|
|
|
```powershell
|
|
flutter devices # confirm your phone is listed
|
|
flutter run # build & install on the connected phone
|
|
```
|
|
|
|
1. On first launch, enter the **API Server URL**, your **email/password**, and a
|
|
**device name**, then tap *Sign in & register device*.
|
|
- Emulator → host: use `http://10.0.2.2:8080`.
|
|
- Physical phone → use the host's LAN IP, e.g. `http://10.2.1.x:8080`
|
|
(the same network as the phone; make sure the API Server is reachable).
|
|
2. On the home screen, **grant SMS & phone permissions**, then **Start gateway**.
|
|
3. Send a test message from the Web App → it appears in the activity log and is
|
|
delivered via the phone. Texts received by the phone show up in the Web App
|
|
**Inbox**.
|
|
|
|
## How it maps to the API
|
|
|
|
| Action | Endpoint |
|
|
|---|---|
|
|
| Login | `POST /api/auth/login` (JWT) |
|
|
| Register device | `POST /api/mobile/v1/device` → device token |
|
|
| Poll pending | `GET /api/mobile/v1/messages` (marks them `Processed`) |
|
|
| Report state | `PATCH /api/mobile/v1/messages/{id}` (`Sent`/`Failed`) |
|
|
| Incoming SMS | `POST /api/mobile/v1/inbox` |
|
|
| Heartbeat | `POST /api/mobile/v1/ping` |
|
|
|
|
Pulled items carry a `type` of `sms` or `call`. For `call` the app places a
|
|
native phone call via `TelecomManager.placeCall` (needs `CALL_PHONE` — covered by
|
|
the phone permission) instead of sending SMS, then reports `Sent`.
|
|
`TelecomManager.placeCall` is used rather than `startActivity(ACTION_CALL)` so the
|
|
call still goes through when the screen is locked / the app is backgrounded
|
|
(Android blocks background activity starts, but not telecom-routed calls).
|
|
|
|
## Code layout
|
|
|
|
```
|
|
lib/
|
|
main.dart entry, bootstraps services, picks first screen
|
|
config.dart default API base + poll/ping intervals
|
|
models/message.dart outbound message model
|
|
services/
|
|
storage.dart persisted settings/tokens (shared_preferences)
|
|
api_client.dart API Server HTTP client
|
|
sms_service.dart platform-channel bridge (send + incoming stream)
|
|
gateway_service.dart the poll → send → report loop + inbox forwarding
|
|
screens/
|
|
login_screen.dart login + device registration
|
|
home_screen.dart start/stop, permissions, activity log
|
|
android_overlay/ native Android files to copy after `flutter create`
|
|
```
|
|
|
|
## Background & delivery reports (implemented)
|
|
|
|
- **Foreground service** (`GatewayForegroundService.kt`): starting the gateway
|
|
launches an ongoing-notification foreground service + partial wakelock, so the
|
|
poll/send loop keeps running while the screen is off or the app is backgrounded.
|
|
- **Delivery reports**: `MainActivity.sendSms` attaches `sent`/`delivered`
|
|
PendingIntents; `SmsStatusReceiver` forwards the outcome (tagged with the
|
|
message id) to Dart, which reports `Delivered`/`Failed` to the API Server.
|
|
|
|
## Known next steps (not yet implemented)
|
|
|
|
- **Survive full task removal / long Doze**: the foreground service covers
|
|
screen-lock, but surviving the user swiping the app away or hours of Doze would
|
|
need a dedicated background Dart isolate (e.g. `flutter_background_service`).
|
|
- **Push wake-up (FCM)**: register an FCM token at registration so the server can
|
|
wake the device instead of polling. Requires a Firebase project +
|
|
`google-services.json` + server-side FCM sending in the API Server.
|
|
- **MMS / data SMS**: only text SMS is implemented.
|