feat(app): 1:1 voice and video calls

End-to-end encrypted calls between two contacts over WebRTC. Call
signalling (offer, answer, hang-up) travels as ordinary Signal-encrypted
messages through the relay; media is DTLS-SRTP, peer to peer when
possible and through the operator's TURN server otherwise.

- Non-trickle ICE (candidates gathered for up to 3 s), 45 s ring
  timeout, busy handling, and a call log entry in the conversation.
- The system call UI rings even when the app is closed: CallKit from a
  PushKit VoIP push on iOS, a full-screen incoming call from an FCM
  wake-up on Android. The wake-up carries no caller; the app learns who
  is calling from the queued offer once it is unlocked.
- New permissions: camera for video, audio routing and Bluetooth
  headsets, network state; iOS background modes audio and voip.

Push on Android: a fresh install now picks UnifiedPush when a
distributor is installed and FCM otherwise, instead of leaving wake-ups
off. Without a wake-up Android puts the app to sleep and messages wait
until it is opened. Users can switch at any time in Settings.
This commit is contained in:
Julius Joska
2026-10-03 22:44:39 +02:00
parent 9a638545dd
commit 933a74c900
17 changed files with 1310 additions and 24 deletions
+4 -2
View File
@@ -5,9 +5,11 @@
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
<string>audio</string>
<string>voip</string>
</array>
<key>NSCameraUsageDescription</key>
<string>The camera is used to scan contacts' QR codes and to take photos for attachments.</string>
<string>The camera is used to scan contacts' QR codes, take photos for attachments and for video calls.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>The photo library is used to pick photos to send in a conversation.</string>
<key>NSFaceIDUsageDescription</key>
@@ -66,6 +68,6 @@
<key>UIStatusBarHidden</key>
<false/>
<key>NSMicrophoneUsageDescription</key>
<string>Šepot needs the microphone to record voice messages.</string>
<string>Šepot needs the microphone for voice messages and calls.</string>
</dict>
</plist>