Files
Julius Joska 933a74c900 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.
2026-10-03 22:44:39 +02:00

42 lines
1.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:sepot/features/calls/call_screen.dart';
import 'package:sepot/l10n/generated/app_localizations.dart';
import 'package:sepot/models/call_log.dart';
/// Ikona a text záznamu hovoru — sdílí chat i seznam chatů, ať se
/// formulace nerozejdou.
({IconData icon, String text, bool alert}) callLogLabel(
AppLocalizations l10n, CallLog log) {
final icon = log.video ? Icons.videocam_rounded : Icons.call_rounded;
return switch (log.outcome) {
CallOutcome.completed => (
icon: icon,
text: log.video
? l10n.callLogVideo(formatCallDuration(log.durationSeconds))
: l10n.callLogVoice(formatCallDuration(log.durationSeconds)),
alert: false,
),
CallOutcome.missed => (
icon: Icons.phone_missed_rounded,
text: l10n.callLogMissed,
alert: true,
),
CallOutcome.unanswered => (
icon: icon,
text: l10n.callLogUnanswered,
alert: false,
),
CallOutcome.rejected => (
icon: Icons.call_end_rounded,
text: l10n.callLogRejected,
alert: false,
),
CallOutcome.busy => (icon: icon, text: l10n.callLogBusy, alert: false),
CallOutcome.failed => (
icon: Icons.error_outline_rounded,
text: l10n.callLogFailed,
alert: true,
),
};
}