# CrossPoint Companion Deep Link API This document describes the deep link functionality that allows the CrossPoint Companion Android app to be launched from QR codes displayed on CrossPoint e-reader devices. ## Overview The CrossPoint firmware can generate QR codes containing deep link URLs. When scanned with a mobile device, these URLs launch the companion app directly to a specific tab and optionally auto-connect to the device. ## URL Scheme ``` crosspoint://? ``` ### Components | Component | Description | |-----------|-------------| | `crosspoint://` | Custom URL scheme registered by the app | | `` | Target tab in the app (see [Path Mapping](#path-mapping)) | | `` | Optional device connection parameters | ## Path Mapping The URL path determines which tab the app navigates to: | Path | App Tab | Description | |------|---------|-------------| | `files` | Device | File browser for device storage | | `library` | Library | Local book library | | `lists` | Lists | Reading lists management | | `settings` | Settings | App settings | **Note:** Unknown paths default to the Library tab. ## Query Parameters Query parameters provide device connection information for automatic connection: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `host` | string | *(required for auto-connect)* | IP address or hostname of the device | | `port` | integer | `80` | HTTP API port | | `wsPort` | integer | `81` | WebSocket port for file uploads | ## URL Examples ### Basic Navigation (No Auto-Connect) Navigate to a specific tab without connecting to a device: ``` crosspoint://files crosspoint://library crosspoint://lists crosspoint://settings ``` ### Auto-Connect to Device Navigate to Device tab and auto-connect: ``` crosspoint://files?host=192.168.1.100 crosspoint://files?host=192.168.1.100&port=80&wsPort=81 ``` ### Custom Ports Connect to a device with non-default ports: ``` crosspoint://files?host=192.168.1.100&port=8080&wsPort=8081 ``` ### Hostname Instead of IP ``` crosspoint://files?host=crosspoint.local&port=80&wsPort=81 ``` ## Firmware Implementation ### QR Code Generation The CrossPoint firmware should generate QR codes containing the deep link URL. Example format: ``` crosspoint://files?host=&port=&wsPort= ``` Where: - `` is the device's current IP address (e.g., from WiFi connection) - `` is the HTTP API port (default: 80) - `` is the WebSocket port (default: 81) ### Example Firmware Code (C++) ```cpp String generateDeepLinkUrl(const char* path = "files") { String url = "crosspoint://"; url += path; url += "?host="; url += WiFi.localIP().toString(); url += "&port="; url += String(HTTP_PORT); // e.g., 80 url += "&wsPort="; url += String(WS_PORT); // e.g., 81 return url; } // Generate QR code with: // String url = generateDeepLinkUrl("files"); // displayQRCode(url); ``` ## App Behavior ### Launch Scenarios #### 1. App Not Running When the app is launched via deep link: 1. App starts and parses the deep link URL 2. Navigates to the target tab 3. If device connection info is present and target is "files": - Checks for existing device with matching IP - If found: uses existing device (preserving user's custom name) - If not found: creates temporary connection - Attempts to connect automatically #### 2. App Already Running When a deep link is received while the app is open: 1. `onNewIntent` receives the new URL 2. Navigates to the target tab 3. Handles device connection (same as above) ### Device Matching Logic When connecting via deep link: ``` 1. Look up device by IP address in database 2. If device exists: a. Check if ports match b. If ports differ, update the stored device with new ports c. Connect using the existing device (preserves custom name) 3. If device doesn't exist: a. Create temporary Device object (not saved to database) b. Connect using temporary device c. Display as "CrossPoint ()" ``` ### Error Handling | Scenario | Behavior | |----------|----------| | Malformed URL | App opens to Library tab (default) | | Unknown path | App opens to Library tab with warning logged | | Invalid host format | Navigation succeeds, no auto-connect | | Invalid port values | Default ports used (80, 81) | | Connection failure | Error message displayed, user can retry | | Device unreachable | Error message with device IP shown | ## Android Implementation Details ### Intent Filter (AndroidManifest.xml) ```xml ``` ### Key Classes | Class | Purpose | |-------|---------| | `DeepLinkParser` | Parses URI into `DeepLinkData` | | `DeepLinkData` | Data class holding parsed deep link info | | `DeviceConnectionInfo` | Data class for host/port/wsPort | | `MainActivity` | Handles incoming intents | | `CrossPointApp` | Routes navigation based on deep link | | `DeviceBrowserViewModel` | Handles `connectFromDeepLink()` | ### Data Flow ``` QR Code Scan │ ▼ Android Intent (ACTION_VIEW) │ ▼ MainActivity.onCreate() / onNewIntent() │ ▼ DeepLinkParser.parse(uri) │ ▼ DeepLinkData { targetTab, deviceConnection? } │ ▼ CrossPointApp (LaunchedEffect) │ ├─► Navigate to targetTab │ └─► If targetTab == Device && deviceConnection != null │ ▼ DeviceBrowserScreen │ ▼ DeviceBrowserViewModel.connectFromDeepLink() │ ├─► Check existing device by IP ├─► Update ports if needed └─► Connect and load files ``` ## Validation Rules ### Host Validation Valid hosts: - IPv4 addresses: `192.168.1.100`, `10.0.0.1` - Hostnames: `crosspoint.local`, `my-device` Invalid hosts (rejected): - Empty strings - Malformed IPs: `192.168.1.256`, `192.168.1` - IPs with invalid octets ### Port Validation - Valid range: 1-65535 - Out-of-range values default to 80 (HTTP) or 81 (WebSocket) - Non-numeric values default to standard ports ## Testing ### Manual Testing with ADB Test deep links without a QR code using ADB: ```bash # Basic navigation adb shell am start -a android.intent.action.VIEW -d "crosspoint://files" adb shell am start -a android.intent.action.VIEW -d "crosspoint://library" # With device connection adb shell am start -a android.intent.action.VIEW -d "crosspoint://files?host=192.168.1.100" adb shell am start -a android.intent.action.VIEW -d "crosspoint://files?host=192.168.1.100&port=80&wsPort=81" # Test while app is running (onNewIntent) adb shell am start -a android.intent.action.VIEW -d "crosspoint://settings" ``` ### Test Cases 1. **Valid deep link with connection info** - URL: `crosspoint://files?host=192.168.1.100&port=80&wsPort=81` - Expected: Opens Device tab, auto-connects to device 2. **Valid deep link without connection info** - URL: `crosspoint://files` - Expected: Opens Device tab, shows device selection 3. **Unknown path** - URL: `crosspoint://unknown` - Expected: Opens Library tab (default) 4. **Missing host parameter** - URL: `crosspoint://files?port=80` - Expected: Opens Device tab, no auto-connect 5. **Invalid host format** - URL: `crosspoint://files?host=invalid..host` - Expected: Opens Device tab, no auto-connect 6. **Device already exists in database** - Precondition: Device with IP 192.168.1.100 saved as "My Reader" - URL: `crosspoint://files?host=192.168.1.100` - Expected: Connects using "My Reader" name 7. **Existing device with different ports** - Precondition: Device saved with port=80, wsPort=81 - URL: `crosspoint://files?host=192.168.1.100&port=8080&wsPort=8081` - Expected: Updates device ports, then connects ## Security Considerations 1. **Local Network Only**: Deep links should only contain local network addresses. The app does not validate this, but firmware should only generate URLs with local IPs. 2. **No Authentication**: The deep link does not include authentication. Device security relies on network-level access control. 3. **Temporary Devices**: Devices created from deep links (when no matching device exists) are not persisted, preventing automatic accumulation of device entries. 4. **No Sensitive Data**: Deep link URLs should not contain sensitive information as QR codes can be photographed. ## Changelog | Version | Changes | |---------|---------| | 1.0.0 | Initial deep link support with `crosspoint://` scheme |