A CTI system for receiving an incoming call on your smartphone as a desktop notification on your PC. Clicking the notification opens a URL with the caller's number passed in (for example, a customer-management system).
sequenceDiagram
participant A as Android app
participant S as Relay server
participant W as Windows resident app
W->>S: Connect via SignalR and join a group
Note over A: Detects an incoming call
A->>S: POST /api/calls/incoming
S->>W: Deliver the call over SignalR
Note over W: Show a toast notification
W->>W: On click, open the configured URL
Only devices whose group ID and shared key match exchange notifications. A single server can serve multiple sites or teams.
| Project | Description | Target |
|---|---|---|
src/SmartPhoneCti.Server |
Server that relays incoming calls | .NET 10 / ASP.NET Core + SignalR |
src/SmartPhoneCti.Desktop |
Windows system-tray resident app | .NET 10 / Windows Forms |
src/SmartPhoneCti.Android |
Android app that detects and forwards calls | .NET 10 for Android (API 26+) |
-
The
androidworkload to build the Android appdotnet workload install android
dotnet run --project src/SmartPhoneCti.ServerIt listens on http://localhost:5062. Open it in a browser to see the status.
dotnet run --project src/SmartPhoneCti.DesktopIt stays resident in the system tray. On first run the settings screen opens; enter:
| Item | Description | Default |
|---|---|---|
| Server URL | Base URL of the relay server | http://localhost:5062 |
| Group ID | The unit that shares notifications | 1 |
| Shared key | The group's passphrase; set it to the same value as the phone side | (empty) |
| URL to open on a call | {deviceId} and {tel} are replaced with the actual values |
sample page |
Settings are saved to %LOCALAPPDATA%\SmartPhoneCTI\settings.json. The tray icon is green when connected and red when disconnected.
dotnet build src/SmartPhoneCti.Android -t:RunOpen the app, set the same group ID and shared key as the PC side, and tap "Start forwarding". When connecting from an emulator to a server on your machine, the server URL is http://10.0.2.2:5062 (the special address that points from the emulator to the host PC).
Even without an Android device, you can test the PC-side notification by posting an incoming call directly:
curl -X POST http://localhost:5062/api/calls/incoming -H "Content-Type: application/json" -d "{\"groupId\":1,\"accessKey\":\"your-key\",\"deviceId\":1,\"callerNumber\":\"0312345678\"}"delivered returns the number of clients the call was delivered to.
| Endpoint | Purpose |
|---|---|
POST /api/calls/incoming |
Notify an incoming call; the body has groupId / accessKey / deviceId / callerNumber |
GET /api/health |
Health check and current connection count |
/hubs/incoming-call |
The SignalR hub the desktop clients connect to |
GET /sample/orders |
Sample of the page opened when a notification is clicked |
- Use a long, hard-to-guess shared key. Authentication relies on this key alone.
- Always serve over HTTPS in production. The app does not perform redirects, so enforce HTTPS at the reverse proxy or App Service level.
- The caller number is personal data. The server log masks all but the last four digits.
- Call-log permission is required. On Android 10 and later, obtaining the caller number from the incoming-call broadcast requires
READ_CALL_LOG. Distributing via Google Play requires a separate declaration for this permission (not needed if you build and install it yourself). ACallScreeningServiceapproach that avoids this permission also exists, but it conflicts with spam-blocking apps, so it is not used (see the development notes for a comparison). - Withheld (anonymous) calls cannot be forwarded, because the caller number is unavailable.
- Depending on Windows notification settings, the toast may not appear. If so, check that SmartPhoneCTI is allowed under Settings > System > Notifications.
- The URL to open on a call is a Windows-side setting; the Android side only notifies of the incoming call.
Use the scripts in tools/ to regenerate icons or capture screenshots of the settings screen. See tools/README.md for details.