mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-16 13:10:20 +00:00
merge master into badge-in-app-checkout
This commit is contained in:
@@ -128,7 +128,7 @@ Common Module (commonMain)
|
||||
| Chat Model | [`ChatModel.kt`](../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L86) | `object ChatModel` | 86 |
|
||||
| App Preferences | [`SimpleXAPI.kt`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L102) | `class AppPreferences` | 102 |
|
||||
| Platform Interface | [`Platform.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Platform.kt#L15) | `interface PlatformInterface` | 15 |
|
||||
| Notification Manager | [`NtfManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L19) | `abstract class NtfManager` | 19 |
|
||||
| Notification Manager | [`NtfManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L20) | `abstract class NtfManager` | 20 |
|
||||
| Theme Manager | [`ThemeManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt#L18) | `object ThemeManager` | 18 |
|
||||
| Android Haskell Init | [`AppCommon.android.kt`](../common/src/androidMain/kotlin/chat/simplex/common/platform/AppCommon.android.kt#L33) | `fun initHaskell(packageName: String)` | 33 |
|
||||
| Common Migrations | [`AppCommon.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt#L41) | `fun runMigrations()` | 41 |
|
||||
|
||||
@@ -121,8 +121,8 @@ App links use the app's own scheme, `simplexchat:`, for everything that is not a
|
||||
| `SwitchingUsersView` | User switch in progress | Loading overlay |
|
||||
| Auth gate | `userAuthorized != true` | `AuthView` or `SplashView` + passcode |
|
||||
| Active call | `showCallView == true` | `ActiveCallView` (desktop) or call activity (Android) |
|
||||
| One-time passcode | Always | `ModalManager.fullscreen.showOneTimePasscodeInView` |
|
||||
| Privacy alerts | Always | `AlertManager.privacySensitive` |
|
||||
| One-time passcode | `userAuthorized == true` | `ModalManager.fullscreen.showOneTimePasscodeInView` |
|
||||
| Privacy alerts | `userAuthorized == true` | `AlertManager.privacySensitive` |
|
||||
| Incoming call | `activeCallInvitation != null` | `IncomingCallAlertView` |
|
||||
| Shared alerts | Always | `AlertManager.shared` |
|
||||
|
||||
@@ -300,7 +300,7 @@ object AppLock {
|
||||
### Authentication Flow
|
||||
|
||||
1. **MainScreen** checks `unauthorized` (derived: `userAuthorized.value != true`) at line ~135.
|
||||
2. If unauthorized and not in an active call:
|
||||
2. If unauthorized, including during an active call:
|
||||
- Launches `AppLock.runAuthenticate()` which triggers platform-specific biometric/passcode prompt.
|
||||
- On Android with system auth finishing during activity destruction, authentication is skipped.
|
||||
3. If `performLA` preference is set and `laFailed` is true: shows `AuthView` with "Unlock" button.
|
||||
@@ -308,7 +308,7 @@ object AppLock {
|
||||
|
||||
### Lock Delay
|
||||
|
||||
The `laLockDelay` preference controls how long after backgrounding the app requires re-authentication. When `laLockDelay == 0`, screen rotation triggers a 3-second grace period (line ~270) to prevent unnecessary re-auth.
|
||||
The `laLockDelay` preference controls how long after backgrounding the app requires re-authentication. When `laLockDelay == 0`, screen rotation triggers a 3-second grace period (line ~270) to prevent unnecessary re-auth. A call counts as activity: `recheckAuthState()` is a no-op while `showCallView` is true, and `CallManager.endCall()` resets `enteredBackground`, so the delay is counted from the end of the call. When a call is accepted from a notification or from the Android lock screen, `recheckAuthState()` is called before `acceptIncomingCall()`, so an expired delay still locks the app.
|
||||
|
||||
### Lock Modes
|
||||
|
||||
|
||||
@@ -59,18 +59,18 @@ State transitions are driven by `WCallResponse` messages from the WebRTC layer.
|
||||
|
||||
## 3. Android Implementation
|
||||
|
||||
### 3.1 CallActivity.kt (464 lines)
|
||||
### 3.1 CallActivity.kt (468 lines)
|
||||
|
||||
[`CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt)
|
||||
|
||||
A dedicated `ComponentActivity` that hosts the call UI. Key responsibilities:
|
||||
|
||||
- **Intent handling** ([line 64](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L64)): On `AcceptCallAction` intent, looks up the matching `RcvCallInvitation` and calls `callManager.acceptIncomingCall()`.
|
||||
- **Lock screen support** ([line 160](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L160)): `unlockForIncomingCall()` uses `setShowWhenLocked(true)` / `setTurnScreenOn(true)` on API 27+, falls back to window flags on older versions. `lockAfterIncomingCall()` reverses these settings.
|
||||
- **Picture-in-Picture** ([line 99](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L99)): `setPipParams()` configures PiP aspect ratio and source rect hint. On Android 12+ (`Build.VERSION_CODES.S`), auto-enter PiP is enabled for video calls. `onPictureInPictureModeChanged` toggles `activeCallViewIsCollapsed` and sends a `WCallCommand.Layout` command.
|
||||
- **Permission checks** ([line 122](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L122)): Checks `RECORD_AUDIO` and conditionally `CAMERA` permissions.
|
||||
- **Service binding** ([line 181](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L181)): Binds to `CallService` as a workaround for Android 12 background activity launch restrictions.
|
||||
- **CallActivityView composable** ([line 208](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L208)): Renders `ActiveCallView()` when permissions are granted and a call is active. Shows `CallPermissionsView` when permissions are needed. Shows `IncomingCallLockScreenAlert` for incoming calls on the lock screen.
|
||||
- **Intent handling** ([line 65](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L65)): On `AcceptCallAction` intent, looks up the matching `RcvCallInvitation` and calls `callManager.acceptIncomingCall()`.
|
||||
- **Lock screen support** ([line 161](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L161)): `unlockForIncomingCall()` uses `setShowWhenLocked(true)` / `setTurnScreenOn(true)` on API 27+, falls back to window flags on older versions. `lockAfterIncomingCall()` reverses these settings.
|
||||
- **Picture-in-Picture** ([line 100](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L100)): `setPipParams()` configures PiP aspect ratio and source rect hint. On Android 12+ (`Build.VERSION_CODES.S`), auto-enter PiP is enabled for video calls. `onPictureInPictureModeChanged` toggles `activeCallViewIsCollapsed` and sends a `WCallCommand.Layout` command.
|
||||
- **Permission checks** ([line 123](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L123)): Checks `RECORD_AUDIO` and conditionally `CAMERA` permissions.
|
||||
- **Service binding** ([line 182](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L182)): Binds to `CallService` as a workaround for Android 12 background activity launch restrictions.
|
||||
- **CallActivityView composable** ([line 209](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L209)): Renders `ActiveCallView()` when permissions are granted and a call is active. Shows `CallPermissionsView` when permissions are needed. Shows `IncomingCallLockScreenAlert` for incoming calls on the lock screen.
|
||||
|
||||
### 3.2 CallService.kt (207 lines)
|
||||
|
||||
@@ -113,17 +113,19 @@ The `actual` platform implementation of `ActiveCallView()` and supporting compos
|
||||
|
||||
## 4. Desktop Implementation
|
||||
|
||||
### 4.1 CallView.desktop.kt (263 lines)
|
||||
### 4.1 CallView.desktop.kt (308 lines)
|
||||
|
||||
[`CallView.desktop.kt`](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt)
|
||||
|
||||
Desktop calls run WebRTC in the system browser, not an embedded WebView:
|
||||
|
||||
- **NanoWSD server** ([line 209](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L209)): `startServer()` creates a `NanoWSD` instance bound to `localhost:50395`. If that port is already in use it falls back to an OS-assigned free port (`port 0`); `WebRTCController` reads `server.listeningPort` for the browser URL. The server serves `call.html` from JAR resources at `/assets/www/desktop/call.html` for the path `/simplex/call/`. All other paths serve resources from `/assets/www/`.
|
||||
- **WebSocket communication** ([line 238](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L238)): `MyWebSocket` handles WebSocket frames from the browser. `onMessage` deserializes JSON into `WVAPIMessage` and forwards to the response handler. `onClose` triggers `WCallResponse.End`.
|
||||
- **WebRTCController** ([line 153](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L153)): Starts the server, then opens `http://localhost:<listeningPort>/simplex/call/` (normally `50395`) via `LocalUriHandler`. Processes `WCallCommand` queue by sending JSON over WebSocket to all active connections. On dispose, sends `WCallCommand.End` and stops the server.
|
||||
- **SendStateUpdates** ([line 137](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L137)): Sends `WCallCommand.Description` with call state and encryption info text to the browser for display.
|
||||
- **ActiveCallView** ([line 28](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L28)): Handles `WCallResponse` messages identically to Android (same state machine), plus a `WCallCommand.Permission` message on `Capabilities` error for browser permission denial guidance.
|
||||
- **NanoWSD server** ([line 215](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L215)): `startServer()` creates a `NanoWSD` instance bound to `localhost:50395`. If that port is already in use it falls back to an OS-assigned free port (`port 0`); `WebRTCController` reads `server.listeningPort` for the browser URL. The server serves `call.html` from JAR resources at `/assets/www/desktop/call.html` for the path `/simplex/call/` when the request includes a valid `token` query parameter. All other paths serve resources from `/assets/www/`.
|
||||
- **Call token** ([line 271](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L271)): `newCallServerToken()` returns 32 random bytes, base64url-encoded. `hasValidCallServerToken()` ([line 277](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L277)) checks the `token` query parameter.
|
||||
- **WebSocket upgrade** ([line 238](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L238)): Each server instance accepts one WebSocket upgrade. The upgrade request must include `Origin: http://localhost:<listeningPort>` and a valid token. Other upgrade requests receive `401 Unauthorized`.
|
||||
- **WebSocket communication** ([line 283](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L283)): `MyWebSocket` handles WebSocket frames from the browser. `onMessage` deserializes JSON into `WVAPIMessage` and forwards to the response handler. `onClose` triggers `WCallResponse.End`.
|
||||
- **WebRTCController** ([line 157](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L157)): Generates the call token, starts the server, then opens `http://localhost:<listeningPort>/simplex/call/?token=<token>` (normally `50395`) via `LocalUriHandler`. Processes `WCallCommand` queue by sending JSON over the WebSocket. On dispose, sends `WCallCommand.End` and stops the server.
|
||||
- **SendStateUpdates** ([line 141](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L141)): Sends `WCallCommand.Description` with call state and encryption info text to the browser for display.
|
||||
- **ActiveCallView** ([line 32](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt#L32)): Handles `WCallResponse` messages identically to Android (same state machine), plus a `WCallCommand.Permission` message on `Capabilities` error for browser permission denial guidance.
|
||||
|
||||
---
|
||||
|
||||
@@ -166,8 +168,8 @@ An in-app notification banner shown when a call invitation arrives while the app
|
||||
|---|---|---|---|
|
||||
| `CallView.kt` | [`common/src/commonMain/.../views/call/CallView.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/CallView.kt) | 28 | `expect fun ActiveCallView()`, delivery receipt waiting |
|
||||
| `CallView.android.kt` | [`common/src/androidMain/.../views/call/CallView.android.kt`](../../common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt) | 891 | Android WebView WebRTC, overlay, permissions |
|
||||
| `CallView.desktop.kt` | [`common/src/desktopMain/.../views/call/CallView.desktop.kt`](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt) | 263 | Desktop browser WebRTC via NanoWSD |
|
||||
| `CallActivity.kt` | [`android/src/main/java/.../views/call/CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt) | 464 | Android call Activity, PiP, lock screen |
|
||||
| `CallView.desktop.kt` | [`common/src/desktopMain/.../views/call/CallView.desktop.kt`](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt) | 308 | Desktop browser WebRTC via NanoWSD |
|
||||
| `CallActivity.kt` | [`android/src/main/java/.../views/call/CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt) | 472 | Android call Activity, PiP, lock screen |
|
||||
| `CallService.kt` | [`android/src/main/java/.../CallService.kt`](../../android/src/main/java/chat/simplex/app/CallService.kt) | 207 | Android foreground service for calls |
|
||||
| `CallManager.kt` | [`common/src/commonMain/.../views/call/CallManager.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt) | 119 | Call lifecycle management |
|
||||
| `WebRTC.kt` | [`common/src/commonMain/.../views/call/WebRTC.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt) | -- | `CallState` enum, `WCallCommand`, `WCallResponse` types |
|
||||
|
||||
@@ -35,16 +35,16 @@ The architecture uses an abstract `NtfManager` in common code with platform-spec
|
||||
|
||||
[`NtfManager.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt) (139 lines, commonMain)
|
||||
|
||||
The global `ntfManager` instance is declared at [line 17](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L17) and initialized by each platform at startup.
|
||||
The global `ntfManager` instance is declared at [line 18](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L18) and initialized by each platform at startup.
|
||||
|
||||
### Concrete methods
|
||||
|
||||
| Method | Line | Description |
|
||||
|---|---|---|
|
||||
| `notifyContactConnected` | [L20](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L20) | Displays "contact connected" notification for a `Contact` |
|
||||
| `notifyContactRequestReceived` | [L27](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L27) | Shows contact request notification with an "Accept" action button |
|
||||
| `notifyMessageReceived` | [L38](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L38) | Conditionally shows message notification based on `ntfsEnabled`, `showNotification`, and whether user is viewing that chat |
|
||||
| `acceptContactRequestAction` | [L51](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L51) | Accepts a contact request from a notification action |
|
||||
| `notifyContactConnected` | [L21](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L21) | Displays "contact connected" notification for a `Contact` |
|
||||
| `notifyContactRequestReceived` | [L28](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L28) | Shows contact request notification with an "Accept" action button |
|
||||
| `notifyMessageReceived` | [L39](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L39) | Conditionally shows message notification based on `ntfsEnabled`, `showNotification`, and whether user is viewing that chat |
|
||||
| `acceptContactRequestAction` | [L52](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L52) | Accepts a contact request from a notification action |
|
||||
| `openChatAction` | [L59](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L59) | Opens a specific chat from a notification tap, switching user if needed |
|
||||
| `showChatsAction` | [L74](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L74) | Opens the chat list, switching user if needed |
|
||||
| `acceptCallAction` | [L88](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L88) | Accepts a call invitation from a notification action |
|
||||
@@ -101,7 +101,7 @@ Channel creation happens in `createNtfChannelsMaybeShowAlert()` ([line 298](../.
|
||||
[Line 160](../../android/src/main/java/chat/simplex/app/model/NtfManager.android.kt#L160):
|
||||
|
||||
- Returns `false` (no notification) if app is in foreground -- in-app alert is used instead.
|
||||
- **Lock screen / screen off**: Uses `setFullScreenIntent` with a `PendingIntent` to `CallActivity`, plus `VISIBILITY_PUBLIC`.
|
||||
- **Lock screen / screen off**: Uses `setFullScreenIntent` with a `PendingIntent` to `CallActivity`.
|
||||
- **Foreground / unlocked**: Uses regular notification with Accept/Reject action buttons and a custom ringtone (`ring_once` raw resource).
|
||||
- Notification flags include `FLAG_INSISTENT` for repeating sound and vibration.
|
||||
- Call notification channel vibration pattern: `[250, 250, 0, 2600]` ms.
|
||||
|
||||
Reference in New Issue
Block a user