mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Conference updates
This commit is contained in:
parent
e6fc58d797
commit
f9191aba6b
@ -926,13 +926,17 @@ private final class NotificationServiceHandler {
|
|||||||
var localContactId: String?
|
var localContactId: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConferenceCallData {
|
struct GroupCallData {
|
||||||
var id: Int64
|
var id: Int64
|
||||||
var updates: String
|
var fromId: PeerId
|
||||||
|
var fromTitle: String
|
||||||
|
var isVideo: Bool
|
||||||
|
var messageId: Int32
|
||||||
|
var accountId: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var callData: CallData?
|
var callData: CallData?
|
||||||
var conferenceCallData: ConferenceCallData?
|
var groupCallData: GroupCallData?
|
||||||
|
|
||||||
if let messageIdString = payloadJson["msg_id"] as? String {
|
if let messageIdString = payloadJson["msg_id"] as? String {
|
||||||
messageId = Int32(messageIdString)
|
messageId = Int32(messageIdString)
|
||||||
@ -959,20 +963,24 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let locKey = payloadJson["loc-key"] as? String, (locKey == "CONF_CALL_REQUEST" || locKey == "CONF_CALL_MISSED"), let callIdString = payloadJson["call_id"] as? String {
|
#if DEBUG
|
||||||
if let callId = Int64(callIdString) {
|
if let locKey = payloadJson["loc-key"] as? String, locKey == "CONF_CALL_REQUEST" {
|
||||||
if let updates = payloadJson["updates"] as? String {
|
|
||||||
var updateString = updates
|
|
||||||
updateString = updateString.replacingOccurrences(of: "-", with: "+")
|
|
||||||
updateString = updateString.replacingOccurrences(of: "_", with: "/")
|
|
||||||
while updateString.count % 4 != 0 {
|
|
||||||
updateString.append("=")
|
|
||||||
}
|
|
||||||
if let updateData = Data(base64Encoded: updateString) {
|
|
||||||
if let callUpdate = AccountStateManager.extractIncomingCallUpdate(data: updateData) {
|
|
||||||
let _ = callUpdate
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if let peerId, let locKey = payloadJson["loc-key"] as? String, (locKey == "CONF_CALL_REQUEST" || locKey == "CONF_VIDEOCALL_REQUEST"), let callIdString = payloadJson["call_id"] as? String, let messageIdString = payloadJson["msg_id"] as? String {
|
||||||
|
if let callId = Int64(callIdString), let messageId = Int32(messageIdString) {
|
||||||
|
if let fromTitle = payloadJson["call_conference_from"] as? String {
|
||||||
|
let isVideo = locKey == "CONF_VIDEOCALL_REQUEST"
|
||||||
|
|
||||||
|
groupCallData = GroupCallData(
|
||||||
|
id: callId,
|
||||||
|
fromId: peerId,
|
||||||
|
fromTitle: fromTitle,
|
||||||
|
isVideo: isVideo,
|
||||||
|
messageId: messageId,
|
||||||
|
accountId: recordId.int64
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let callIdString = payloadJson["call_id"] as? String, let callAccessHashString = payloadJson["call_ah"] as? String, let peerId = peerId, let updates = payloadJson["updates"] as? String {
|
} else if let callIdString = payloadJson["call_id"] as? String, let callAccessHashString = payloadJson["call_ah"] as? String, let peerId = peerId, let updates = payloadJson["updates"] as? String {
|
||||||
@ -1011,12 +1019,15 @@ private final class NotificationServiceHandler {
|
|||||||
case readMessage(MessageId)
|
case readMessage(MessageId)
|
||||||
case readStories(peerId: PeerId, maxId: Int32)
|
case readStories(peerId: PeerId, maxId: Int32)
|
||||||
case call(CallData)
|
case call(CallData)
|
||||||
|
case groupCall(GroupCallData)
|
||||||
}
|
}
|
||||||
|
|
||||||
var action: Action?
|
var action: Action?
|
||||||
|
|
||||||
if let callData = callData {
|
if let callData = callData {
|
||||||
action = .call(callData)
|
action = .call(callData)
|
||||||
|
} else if let groupCallData {
|
||||||
|
action = .groupCall(groupCallData)
|
||||||
} else if let locKey = payloadJson["loc-key"] as? String {
|
} else if let locKey = payloadJson["loc-key"] as? String {
|
||||||
switch locKey {
|
switch locKey {
|
||||||
case "SESSION_REVOKE":
|
case "SESSION_REVOKE":
|
||||||
@ -1265,6 +1276,50 @@ private final class NotificationServiceHandler {
|
|||||||
content.body = "Incoming Call"
|
content.body = "Incoming Call"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCurrentContent(content)
|
||||||
|
completed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case let .groupCall(groupCallData):
|
||||||
|
if let stateManager = strongSelf.stateManager {
|
||||||
|
let content = NotificationContent(isLockedMessage: nil)
|
||||||
|
updateCurrentContent(content)
|
||||||
|
|
||||||
|
let _ = (stateManager.postbox.transaction { transaction -> TelegramUser? in
|
||||||
|
return transaction.getPeer(groupCallData.fromId) as? TelegramUser
|
||||||
|
}).start(next: { fromPeer in
|
||||||
|
var voipPayload: [AnyHashable: Any] = [
|
||||||
|
"group_call_id": "\(groupCallData.id)",
|
||||||
|
"msg_id": "\(groupCallData.messageId)",
|
||||||
|
"video": "0",
|
||||||
|
"from_id": "\(groupCallData.fromId.id._internalGetInt64Value())",
|
||||||
|
"from_title": groupCallData.fromTitle,
|
||||||
|
"accountId": "\(groupCallData.accountId)"
|
||||||
|
]
|
||||||
|
if let phoneNumber = fromPeer?.phone {
|
||||||
|
voipPayload["phoneNumber"] = phoneNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
if #available(iOS 14.5, *), voiceCallSettings.enableSystemIntegration {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will report voip notification")
|
||||||
|
let content = NotificationContent(isLockedMessage: nil)
|
||||||
|
updateCurrentContent(content)
|
||||||
|
|
||||||
|
CXProvider.reportNewIncomingVoIPPushPayload(voipPayload, completion: { error in
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Did report voip notification, error: \(String(describing: error))")
|
||||||
|
|
||||||
|
completed()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
var content = NotificationContent(isLockedMessage: nil)
|
||||||
|
if let peer = fromPeer {
|
||||||
|
content.title = peer.debugDisplayTitle
|
||||||
|
content.body = incomingCallMessage
|
||||||
|
} else {
|
||||||
|
content.body = "Incoming Call"
|
||||||
|
}
|
||||||
|
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
}
|
}
|
||||||
|
@ -82,13 +82,15 @@ public struct PresentationCallState: Equatable {
|
|||||||
public var remoteVideoState: RemoteVideoState
|
public var remoteVideoState: RemoteVideoState
|
||||||
public var remoteAudioState: RemoteAudioState
|
public var remoteAudioState: RemoteAudioState
|
||||||
public var remoteBatteryLevel: RemoteBatteryLevel
|
public var remoteBatteryLevel: RemoteBatteryLevel
|
||||||
|
public var supportsConferenceCalls: Bool
|
||||||
|
|
||||||
public init(state: State, videoState: VideoState, remoteVideoState: RemoteVideoState, remoteAudioState: RemoteAudioState, remoteBatteryLevel: RemoteBatteryLevel) {
|
public init(state: State, videoState: VideoState, remoteVideoState: RemoteVideoState, remoteAudioState: RemoteAudioState, remoteBatteryLevel: RemoteBatteryLevel, supportsConferenceCalls: Bool) {
|
||||||
self.state = state
|
self.state = state
|
||||||
self.videoState = videoState
|
self.videoState = videoState
|
||||||
self.remoteVideoState = remoteVideoState
|
self.remoteVideoState = remoteVideoState
|
||||||
self.remoteAudioState = remoteAudioState
|
self.remoteAudioState = remoteAudioState
|
||||||
self.remoteBatteryLevel = remoteBatteryLevel
|
self.remoteBatteryLevel = remoteBatteryLevel
|
||||||
|
self.supportsConferenceCalls = supportsConferenceCalls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +167,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
self.conferenceAddParticipant?()
|
self.conferenceAddParticipant?()
|
||||||
}
|
}
|
||||||
|
|
||||||
var isConferencePossible = true
|
|
||||||
if let data = self.call.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_enable_conference"] as? Double {
|
|
||||||
isConferencePossible = value != 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
self.callScreenState = PrivateCallScreen.State(
|
self.callScreenState = PrivateCallScreen.State(
|
||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
lifecycleState: .connecting,
|
lifecycleState: .connecting,
|
||||||
@ -185,7 +180,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
remoteVideo: nil,
|
remoteVideo: nil,
|
||||||
isRemoteBatteryLow: false,
|
isRemoteBatteryLow: false,
|
||||||
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency,
|
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency,
|
||||||
isConferencePossible: isConferencePossible
|
isConferencePossible: false
|
||||||
)
|
)
|
||||||
|
|
||||||
self.isMicrophoneMutedDisposable = (call.isMuted
|
self.isMicrophoneMutedDisposable = (call.isMuted
|
||||||
@ -549,6 +544,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
callScreenState.isRemoteAudioMuted = false
|
callScreenState.isRemoteAudioMuted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callScreenState.isConferencePossible = callState.supportsConferenceCalls
|
||||||
|
|
||||||
if self.callScreenState != callScreenState {
|
if self.callScreenState != callScreenState {
|
||||||
self.callScreenState = callScreenState
|
self.callScreenState = callScreenState
|
||||||
self.update(transition: .animated(duration: 0.35, curve: .spring))
|
self.update(transition: .animated(duration: 0.35, curve: .spring))
|
||||||
|
@ -275,6 +275,8 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
private var callWasActive = false
|
private var callWasActive = false
|
||||||
private var shouldPresentCallRating = false
|
private var shouldPresentCallRating = false
|
||||||
|
|
||||||
|
private var supportsConferenceCalls: Bool = false
|
||||||
|
|
||||||
private var previousVideoState: PresentationCallState.VideoState?
|
private var previousVideoState: PresentationCallState.VideoState?
|
||||||
private var previousRemoteVideoState: PresentationCallState.RemoteVideoState?
|
private var previousRemoteVideoState: PresentationCallState.RemoteVideoState?
|
||||||
private var previousRemoteAudioState: PresentationCallState.RemoteAudioState?
|
private var previousRemoteAudioState: PresentationCallState.RemoteAudioState?
|
||||||
@ -444,9 +446,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.isVideo = startWithVideo
|
self.isVideo = startWithVideo
|
||||||
if self.isVideo {
|
if self.isVideo {
|
||||||
self.videoCapturer = OngoingCallVideoCapturer()
|
self.videoCapturer = OngoingCallVideoCapturer()
|
||||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active(isScreencast: self.isScreencastActive, endpointId: ""), remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active(isScreencast: self.isScreencastActive, endpointId: ""), remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal, supportsConferenceCalls: self.supportsConferenceCalls))
|
||||||
} else {
|
} else {
|
||||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal, supportsConferenceCalls: self.supportsConferenceCalls))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.serializedData = serializedData
|
self.serializedData = serializedData
|
||||||
@ -457,11 +459,25 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
|
|
||||||
var didReceiveAudioOutputs = false
|
var didReceiveAudioOutputs = false
|
||||||
|
|
||||||
if let incomingConferenceSource = incomingConferenceSource {
|
if let incomingConferenceSource {
|
||||||
self.sessionStateDisposable = (context.engine.data.subscribe(
|
let isRinging = context.account.callSessionManager.ringingStates()
|
||||||
|
|> map { ringingStates -> Bool in
|
||||||
|
for ringingState in ringingStates {
|
||||||
|
if ringingState.id == internalId {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|> take(1)
|
||||||
|
self.sessionStateDisposable = (combineLatest(queue: .mainQueue(),
|
||||||
|
isRinging,
|
||||||
|
context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Messages.Message(id: incomingConferenceSource)
|
TelegramEngine.EngineData.Item.Messages.Message(id: incomingConferenceSource)
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] message in
|
)
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] isRinging, message in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -485,6 +501,8 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
} else {
|
} else {
|
||||||
state = .terminated(id: nil, reason: .ended(.hungUp), options: CallTerminationOptions())
|
state = .terminated(id: nil, reason: .ended(.hungUp), options: CallTerminationOptions())
|
||||||
}
|
}
|
||||||
|
} else if isRinging {
|
||||||
|
state = .ringing
|
||||||
} else {
|
} else {
|
||||||
state = .terminated(id: nil, reason: .ended(.hungUp), options: CallTerminationOptions())
|
state = .terminated(id: nil, reason: .ended(.hungUp), options: CallTerminationOptions())
|
||||||
}
|
}
|
||||||
@ -817,6 +835,10 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .active(_, _, _, _, _, _, _, _, supportsConferenceCallsValue) = sessionState.state {
|
||||||
|
self.supportsConferenceCalls = supportsConferenceCallsValue
|
||||||
|
}
|
||||||
|
|
||||||
let mappedVideoState: PresentationCallState.VideoState
|
let mappedVideoState: PresentationCallState.VideoState
|
||||||
let mappedRemoteVideoState: PresentationCallState.RemoteVideoState
|
let mappedRemoteVideoState: PresentationCallState.RemoteVideoState
|
||||||
let mappedRemoteAudioState: PresentationCallState.RemoteAudioState
|
let mappedRemoteAudioState: PresentationCallState.RemoteAudioState
|
||||||
@ -883,7 +905,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
|
|
||||||
switch sessionState.state {
|
switch sessionState.state {
|
||||||
case .ringing:
|
case .ringing:
|
||||||
presentationState = PresentationCallState(state: .ringing, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .ringing, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
if previous == nil || previousControl == nil {
|
if previous == nil || previousControl == nil {
|
||||||
if !self.reportedIncomingCall, let stableId = sessionState.stableId {
|
if !self.reportedIncomingCall, let stableId = sessionState.stableId {
|
||||||
self.reportedIncomingCall = true
|
self.reportedIncomingCall = true
|
||||||
@ -922,17 +944,17 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
case .accepting:
|
case .accepting:
|
||||||
self.callWasActive = true
|
self.callWasActive = true
|
||||||
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
case let .dropping(reason):
|
case let .dropping(reason):
|
||||||
if case .ended(.switchedToConference) = reason {
|
if case .ended(.switchedToConference) = reason {
|
||||||
} else {
|
} else {
|
||||||
presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
}
|
}
|
||||||
case let .terminated(id, reason, options):
|
case let .terminated(id, reason, options):
|
||||||
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
case let .requesting(ringing):
|
case let .requesting(ringing):
|
||||||
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
case .active(_, _, _, _, _, _, _, _), .switchedToConference:
|
case .active(_, _, _, _, _, _, _, _, _), .switchedToConference:
|
||||||
self.callWasActive = true
|
self.callWasActive = true
|
||||||
|
|
||||||
var isConference = false
|
var isConference = false
|
||||||
@ -940,12 +962,12 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
isConference = true
|
isConference = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let callContextState = callContextState, !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _) = sessionState.state {
|
if let callContextState = callContextState, !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _, _) = sessionState.state {
|
||||||
switch callContextState.state {
|
switch callContextState.state {
|
||||||
case .initializing:
|
case .initializing:
|
||||||
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
case .failed:
|
case .failed:
|
||||||
presentationState = PresentationCallState(state: .terminating(.error(.disconnected)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .terminating(.error(.disconnected)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
self.callSessionManager.drop(internalId: self.internalId, reason: .disconnect, debugLog: .single(nil))
|
self.callSessionManager.drop(internalId: self.internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
case .connected:
|
case .connected:
|
||||||
let timestamp: Double
|
let timestamp: Double
|
||||||
@ -955,7 +977,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
timestamp = CFAbsoluteTimeGetCurrent()
|
timestamp = CFAbsoluteTimeGetCurrent()
|
||||||
self.activeTimestamp = timestamp
|
self.activeTimestamp = timestamp
|
||||||
}
|
}
|
||||||
presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
case .reconnecting:
|
case .reconnecting:
|
||||||
let timestamp: Double
|
let timestamp: Double
|
||||||
if let activeTimestamp = self.activeTimestamp {
|
if let activeTimestamp = self.activeTimestamp {
|
||||||
@ -964,10 +986,10 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
timestamp = CFAbsoluteTimeGetCurrent()
|
timestamp = CFAbsoluteTimeGetCurrent()
|
||||||
self.activeTimestamp = timestamp
|
self.activeTimestamp = timestamp
|
||||||
}
|
}
|
||||||
presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
}
|
}
|
||||||
} else if !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _) = sessionState.state {
|
} else if !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _, _) = sessionState.state {
|
||||||
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,7 +1047,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
subscribedToScheduled: false,
|
subscribedToScheduled: false,
|
||||||
isStream: false
|
isStream: false
|
||||||
), conferenceCallData),
|
), conferenceCallData),
|
||||||
internalId: CallSessionInternalId(),
|
internalId: self.internalId,
|
||||||
peerId: nil,
|
peerId: nil,
|
||||||
isChannel: false,
|
isChannel: false,
|
||||||
invite: nil,
|
invite: nil,
|
||||||
@ -1155,7 +1177,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
if let _ = audioSessionControl {
|
if let _ = audioSessionControl {
|
||||||
self.audioSessionShouldBeActive.set(true)
|
self.audioSessionShouldBeActive.set(true)
|
||||||
}
|
}
|
||||||
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P):
|
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P, _):
|
||||||
self.audioSessionShouldBeActive.set(true)
|
self.audioSessionShouldBeActive.set(true)
|
||||||
|
|
||||||
if conferenceCallData != nil {
|
if conferenceCallData != nil {
|
||||||
|
@ -1146,7 +1146,7 @@ public final class AccountStateManager {
|
|||||||
strongSelf.reportMessageDeliveryDisposable.add(_internal_reportMessageDelivery(postbox: strongSelf.postbox, network: strongSelf.network, messageIds: Array(events.reportMessageDelivery), fromPushNotification: false).start())
|
strongSelf.reportMessageDeliveryDisposable.add(_internal_reportMessageDelivery(postbox: strongSelf.postbox, network: strongSelf.network, messageIds: Array(events.reportMessageDelivery), fromPushNotification: false).start())
|
||||||
}
|
}
|
||||||
if !events.addedConferenceInvitationMessagesIds.isEmpty {
|
if !events.addedConferenceInvitationMessagesIds.isEmpty {
|
||||||
strongSelf.callSessionManager?.addConferenceInvitationMessages(ids: events.addedConferenceInvitationMessagesIds)
|
strongSelf.callSessionManager?.addConferenceInvitationMessages(ids: events.addedConferenceInvitationMessagesIds.map { ($0, nil) })
|
||||||
}
|
}
|
||||||
if !events.isContactUpdates.isEmpty {
|
if !events.isContactUpdates.isEmpty {
|
||||||
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
||||||
|
@ -85,7 +85,7 @@ enum CallSessionInternalState {
|
|||||||
case requesting(a: Data, disposable: Disposable)
|
case requesting(a: Data, disposable: Disposable)
|
||||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
||||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
||||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, supportsConferenceCalls: Bool)
|
||||||
case switchedToConference(slug: String)
|
case switchedToConference(slug: String)
|
||||||
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
||||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||||
@ -104,7 +104,7 @@ enum CallSessionInternalState {
|
|||||||
return id
|
return id
|
||||||
case let .confirming(id, _, _, _, _, _):
|
case let .confirming(id, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .active(id, _, _, _, _, _, _, _, _, _, _):
|
case let .active(id, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case .switchedToConference:
|
case .switchedToConference:
|
||||||
return nil
|
return nil
|
||||||
@ -120,19 +120,36 @@ public typealias CallSessionInternalId = UUID
|
|||||||
typealias CallSessionStableId = Int64
|
typealias CallSessionStableId = Int64
|
||||||
|
|
||||||
private final class StableIncomingUUIDs {
|
private final class StableIncomingUUIDs {
|
||||||
|
private enum Key: Hashable {
|
||||||
|
case global(callId: Int64)
|
||||||
|
case group(peerId: Int64, messageId: Int32)
|
||||||
|
}
|
||||||
|
|
||||||
static let shared = Atomic<StableIncomingUUIDs>(value: StableIncomingUUIDs())
|
static let shared = Atomic<StableIncomingUUIDs>(value: StableIncomingUUIDs())
|
||||||
|
|
||||||
private var dict: [Int64: UUID] = [:]
|
private var dict: [Key: UUID] = [:]
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(id: Int64) -> UUID {
|
func get(id: Int64) -> UUID {
|
||||||
if let value = self.dict[id] {
|
let key = Key.global(callId: id)
|
||||||
|
if let value = self.dict[key] {
|
||||||
return value
|
return value
|
||||||
} else {
|
} else {
|
||||||
let value = UUID()
|
let value = UUID()
|
||||||
self.dict[id] = value
|
self.dict[key] = value
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(peerId: Int64, messageId: Int32) -> UUID {
|
||||||
|
let key = Key.group(peerId: peerId, messageId: messageId)
|
||||||
|
if let value = self.dict[key] {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
let value = UUID()
|
||||||
|
self.dict[key] = value
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +190,7 @@ public enum CallSessionState {
|
|||||||
case ringing
|
case ringing
|
||||||
case accepting
|
case accepting
|
||||||
case requesting(ringing: Bool)
|
case requesting(ringing: Bool)
|
||||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, supportsConferenceCalls: Bool)
|
||||||
case switchedToConference(slug: String)
|
case switchedToConference(slug: String)
|
||||||
case dropping(reason: CallSessionTerminationReason)
|
case dropping(reason: CallSessionTerminationReason)
|
||||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||||
@ -190,8 +207,8 @@ public enum CallSessionState {
|
|||||||
self = .requesting(ringing: true)
|
self = .requesting(ringing: true)
|
||||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, supportsConferenceCalls):
|
||||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, supportsConferenceCalls: supportsConferenceCalls)
|
||||||
case let .dropping(reason, _):
|
case let .dropping(reason, _):
|
||||||
self = .dropping(reason: reason)
|
self = .dropping(reason: reason)
|
||||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||||
@ -379,6 +396,16 @@ private final class CallSessionContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct IncomingConferenceTermporaryExternalInfo {
|
||||||
|
public var callId: Int64
|
||||||
|
public var isVideo: Bool
|
||||||
|
|
||||||
|
public init(callId: Int64, isVideo: Bool) {
|
||||||
|
self.callId = callId
|
||||||
|
self.isVideo = isVideo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class IncomingConferenceInvitationContext {
|
private final class IncomingConferenceInvitationContext {
|
||||||
enum State: Equatable {
|
enum State: Equatable {
|
||||||
case pending
|
case pending
|
||||||
@ -393,19 +420,52 @@ private final class IncomingConferenceInvitationContext {
|
|||||||
|
|
||||||
private(set) var state: State = .pending
|
private(set) var state: State = .pending
|
||||||
|
|
||||||
init(queue: Queue, postbox: Postbox, messageId: MessageId, updated: @escaping () -> Void) {
|
init(queue: Queue, postbox: Postbox, internalId: CallSessionInternalId, messageId: MessageId, externalInfo: IncomingConferenceTermporaryExternalInfo?, updated: @escaping () -> Void) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
self.internalId = CallSessionInternalId()
|
self.internalId = internalId
|
||||||
|
|
||||||
let key = PostboxViewKey.messages(Set([messageId]))
|
let key = PostboxViewKey.messages(Set([messageId]))
|
||||||
self.disposable = (postbox.combinedView(keys: [key])
|
|
||||||
|
if let externalInfo {
|
||||||
|
self.state = .ringing(
|
||||||
|
callId: externalInfo.callId,
|
||||||
|
isVideo: externalInfo.isVideo,
|
||||||
|
otherParticipants: []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let waitSignal: Signal<Void, NoError>
|
||||||
|
if externalInfo != nil {
|
||||||
|
waitSignal = postbox.combinedView(keys: [key])
|
||||||
|
|> mapToSignal { view -> Signal<Void, NoError> in
|
||||||
|
guard let view = view.views[key] as? MessagesView else {
|
||||||
|
return .never()
|
||||||
|
}
|
||||||
|
if view.messages[messageId] == nil {
|
||||||
|
return .never()
|
||||||
|
}
|
||||||
|
return .single(Void())
|
||||||
|
}
|
||||||
|
|> take(1)
|
||||||
|
|> timeout(5.0, queue: self.queue, alternate: deferred {
|
||||||
|
Logger.shared.log("CallSessionManagerContext", "IncomingConferenceInvitationContext timeout for message \(messageId)")
|
||||||
|
return Signal<Void, NoError>.single(Void())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
waitSignal = .single(Void())
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disposable = (waitSignal |> map { _ -> Message? in return nil }
|
||||||
|
|> then(
|
||||||
|
postbox.combinedView(keys: [key])
|
||||||
|> map { view -> Message? in
|
|> map { view -> Message? in
|
||||||
guard let view = view.views[key] as? MessagesView else {
|
guard let view = view.views[key] as? MessagesView else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return view.messages[messageId]
|
return view.messages[messageId]
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] message in
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] message in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
return
|
return
|
||||||
@ -782,7 +842,7 @@ private final class CallSessionManagerContext {
|
|||||||
case let .accepting(id, accessHash, _, _, disposable):
|
case let .accepting(id, accessHash, _, _, disposable):
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _):
|
||||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||||
let internalReason: DropCallSessionReason
|
let internalReason: DropCallSessionReason
|
||||||
switch reason {
|
switch reason {
|
||||||
@ -884,7 +944,7 @@ private final class CallSessionManagerContext {
|
|||||||
var dropData: (CallSessionStableId, Int64)?
|
var dropData: (CallSessionStableId, Int64)?
|
||||||
let isVideo = context.type == .video
|
let isVideo = context.type == .video
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||||
dropData = (id, accessHash)
|
dropData = (id, accessHash)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -934,9 +994,9 @@ private final class CallSessionManagerContext {
|
|||||||
case let .waiting(config):
|
case let .waiting(config):
|
||||||
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P):
|
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P, supportsConferenceCalls):
|
||||||
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, supportsConferenceCalls: supportsConferenceCalls)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -957,7 +1017,7 @@ private final class CallSessionManagerContext {
|
|||||||
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||||
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -973,7 +1033,7 @@ private final class CallSessionManagerContext {
|
|||||||
|
|
||||||
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||||
idAndAccessHash = (id, accessHash)
|
idAndAccessHash = (id, accessHash)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -1097,7 +1157,7 @@ private final class CallSessionManagerContext {
|
|||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||||
@ -1127,13 +1187,14 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters):
|
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters):
|
||||||
let allowsP2P = (flags & (1 << 5)) != 0
|
let allowsP2P = (flags & (1 << 5)) != 0
|
||||||
|
let supportsConferenceCalls = (flags & (1 << 8)) != 0
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
||||||
break
|
break
|
||||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, supportsConferenceCalls):
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, supportsConferenceCalls: supportsConferenceCalls)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||||
@ -1152,7 +1213,7 @@ private final class CallSessionManagerContext {
|
|||||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||||
context.isVideoPossible = isVideoPossible
|
context.isVideoPossible = isVideoPossible
|
||||||
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, supportsConferenceCalls: supportsConferenceCalls)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -1179,7 +1240,7 @@ private final class CallSessionManagerContext {
|
|||||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||||
context.isVideoPossible = isVideoPossible
|
context.isVideoPossible = isVideoPossible
|
||||||
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, supportsConferenceCalls: supportsConferenceCalls)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -1256,10 +1317,12 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addConferenceInvitationMessages(ids: [MessageId]) {
|
func addConferenceInvitationMessages(ids: [(id: MessageId, externalInfo: IncomingConferenceTermporaryExternalInfo?)]) {
|
||||||
for id in ids {
|
var updateRingingStates = false
|
||||||
|
for (id, externalInfo) in ids {
|
||||||
if self.incomingConferenceInvitationContexts[id] == nil {
|
if self.incomingConferenceInvitationContexts[id] == nil {
|
||||||
let context = IncomingConferenceInvitationContext(queue: self.queue, postbox: self.postbox, messageId: id, updated: { [weak self] in
|
Logger.shared.log("CallSessionManagerContext", "Adding incoming conference invitation context for message \(id)")
|
||||||
|
let context = IncomingConferenceInvitationContext(queue: self.queue, postbox: self.postbox, internalId: CallSessionManager.getStableIncomingUUID(peerId: id.peerId.id._internalGetInt64Value(), messageId: id.id), messageId: id, externalInfo: externalInfo, updated: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1275,8 +1338,14 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.incomingConferenceInvitationContexts[id] = context
|
self.incomingConferenceInvitationContexts[id] = context
|
||||||
|
updateRingingStates = true
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("CallSessionManagerContext", "Conference invitation context for message \(id) already exists")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if updateRingingStates {
|
||||||
|
self.ringingStatesUpdated()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
||||||
@ -1350,6 +1419,12 @@ public final class CallSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func getStableIncomingUUID(peerId: Int64, messageId: Int32) -> UUID {
|
||||||
|
return StableIncomingUUIDs.shared.with { impl in
|
||||||
|
return impl.get(peerId: peerId, messageId: messageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
private var contextRef: Unmanaged<CallSessionManagerContext>?
|
private var contextRef: Unmanaged<CallSessionManagerContext>?
|
||||||
|
|
||||||
@ -1388,7 +1463,7 @@ public final class CallSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addConferenceInvitationMessages(ids: [MessageId]) {
|
public func addConferenceInvitationMessages(ids: [(id: MessageId, externalInfo: IncomingConferenceTermporaryExternalInfo?)]) {
|
||||||
self.withContext { context in
|
self.withContext { context in
|
||||||
context.addConferenceInvitationMessages(ids: ids)
|
context.addConferenceInvitationMessages(ids: ids)
|
||||||
}
|
}
|
||||||
@ -1500,7 +1575,7 @@ public final class CallSessionManager {
|
|||||||
|
|
||||||
private enum AcceptedCall {
|
private enum AcceptedCall {
|
||||||
case waiting(config: SecretChatEncryptionConfig)
|
case waiting(config: SecretChatEncryptionConfig)
|
||||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, supportsConferenceCalls: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum AcceptCallResult {
|
private enum AcceptCallResult {
|
||||||
@ -1553,7 +1628,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
|||||||
customParametersValue = data
|
customParametersValue = data
|
||||||
}
|
}
|
||||||
|
|
||||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0))
|
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0, supportsConferenceCalls: (flags & (1 << 8)) != 0))
|
||||||
} else {
|
} else {
|
||||||
return .failed
|
return .failed
|
||||||
}
|
}
|
||||||
|
@ -2058,12 +2058,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
|
|
||||||
if (application.applicationState == .inactive) {
|
|
||||||
Logger.shared.log("App \(self.episodeId)", "tap local notification \(String(describing: notification.userInfo)), applicationState \(application.applicationState)")
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
|
public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
|
||||||
if #available(iOS 9.0, *) {
|
if #available(iOS 9.0, *) {
|
||||||
if case PKPushType.voIP = type {
|
if case PKPushType.voIP = type {
|
||||||
@ -2156,6 +2150,86 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let phoneNumber = payloadJson["phoneNumber"] as? String
|
||||||
|
|
||||||
|
if let fromIdString = payloadJson["from_id"] as? String, let fromId = Int64(fromIdString), let groupCallIdString = payloadJson["group_call_id"] as? String, let groupCallId = Int64(groupCallIdString), let messageIdString = payloadJson["msg_id"] as? String, let messageId = Int32(messageIdString), let isVideoString = payloadJson["video"] as? String, let isVideo = Int32(isVideoString), let fromTitle = payloadJson["from_title"] as? String {
|
||||||
|
guard let callKitIntegration = CallKitIntegration.shared else {
|
||||||
|
Logger.shared.log("App \(self.episodeId) PushRegistry", "CallKitIntegration is not available")
|
||||||
|
completion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(fromId))
|
||||||
|
let messageId = MessageId(peerId: fromPeerId, namespace: Namespaces.Message.Cloud, id: messageId)
|
||||||
|
|
||||||
|
let internalId = CallSessionManager.getStableIncomingUUID(peerId: fromPeerId.id._internalGetInt64Value(), messageId: messageId.id)
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let displayTitle: "\(fromTitle)"
|
||||||
|
|
||||||
|
callKitIntegration.reportIncomingCall(
|
||||||
|
uuid: internalId,
|
||||||
|
stableId: groupCallId,
|
||||||
|
handle: "\(fromPeerId.id._internalGetInt64Value())",
|
||||||
|
phoneNumber: phoneNumber.flatMap(formatPhoneNumber),
|
||||||
|
isVideo: isVideo != 0,
|
||||||
|
displayTitle: displayTitle,
|
||||||
|
completion: { error in
|
||||||
|
if let error = error {
|
||||||
|
if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) {
|
||||||
|
Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode")
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)")
|
||||||
|
/*Queue.mainQueue().async {
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .hangUp, debugLog: .single(nil))
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let _ = (self.sharedContextPromise.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||||
|
let _ = (sharedApplicationContext.sharedContext.activeAccountContexts
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { activeAccounts in
|
||||||
|
var processed = false
|
||||||
|
for (_, context, _) in activeAccounts.accounts {
|
||||||
|
if context.account.id == accountId {
|
||||||
|
context.account.callSessionManager.addConferenceInvitationMessages(ids: [(messageId, IncomingConferenceTermporaryExternalInfo(callId: groupCallId, isVideo: isVideo != 0))])
|
||||||
|
|
||||||
|
/*disposable.set((context.account.callSessionManager.callState(internalId: internalId)
|
||||||
|
|> deliverOnMainQueue).start(next: { state in
|
||||||
|
switch state.state {
|
||||||
|
case .terminated:
|
||||||
|
callKitIntegration.dropCall(uuid: internalId)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}))*/
|
||||||
|
|
||||||
|
processed = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !processed {
|
||||||
|
callKitIntegration.dropCall(uuid: internalId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0)
|
||||||
|
|
||||||
|
if case PKPushType.voIP = type {
|
||||||
|
Logger.shared.log("App \(self.episodeId) PushRegistry", "pushRegistry payload: \(payload.dictionaryPayload)")
|
||||||
|
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
guard var updateString = payloadJson["updates"] as? String else {
|
guard var updateString = payloadJson["updates"] as? String else {
|
||||||
Logger.shared.log("App \(self.episodeId) PushRegistry", "updates is nil")
|
Logger.shared.log("App \(self.episodeId) PushRegistry", "updates is nil")
|
||||||
completion()
|
completion()
|
||||||
@ -2183,8 +2257,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let phoneNumber = payloadJson["phoneNumber"] as? String
|
|
||||||
|
|
||||||
callKitIntegration.reportIncomingCall(
|
callKitIntegration.reportIncomingCall(
|
||||||
uuid: CallSessionManager.getStableIncomingUUID(stableId: callUpdate.callId),
|
uuid: CallSessionManager.getStableIncomingUUID(stableId: callUpdate.callId),
|
||||||
stableId: callUpdate.callId,
|
stableId: callUpdate.callId,
|
||||||
@ -2220,7 +2292,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
context.account.stateManager.processIncomingCallUpdate(data: updateData, completion: { _ in
|
context.account.stateManager.processIncomingCallUpdate(data: updateData, completion: { _ in
|
||||||
})
|
})
|
||||||
|
|
||||||
//callUpdate.callId
|
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
self.watchedCallsDisposables.add(disposable)
|
self.watchedCallsDisposables.add(disposable)
|
||||||
|
|
||||||
@ -2252,6 +2323,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
|
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Logger.shared.log("App \(self.episodeId) PushRegistry", "Invoking completion handler")
|
Logger.shared.log("App \(self.episodeId) PushRegistry", "Invoking completion handler")
|
||||||
|
|
||||||
|
@ -155,6 +155,12 @@ public final class SharedWakeupManager {
|
|||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
let keepUpdatesForCalls = combineLatest(queue: .mainQueue(), hasActiveCalls, hasActiveGroupCalls)
|
||||||
|
|> map { hasActiveCalls, hasActiveGroupCalls -> Bool in
|
||||||
|
return hasActiveCalls || hasActiveGroupCalls
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|
||||||
let isPlayingBackgroundActiveCall = combineLatest(queue: .mainQueue(), hasActiveCalls, hasActiveGroupCalls, hasActiveAudioSession)
|
let isPlayingBackgroundActiveCall = combineLatest(queue: .mainQueue(), hasActiveCalls, hasActiveGroupCalls, hasActiveAudioSession)
|
||||||
|> map { hasActiveCalls, hasActiveGroupCalls, hasActiveAudioSession -> Bool in
|
|> map { hasActiveCalls, hasActiveGroupCalls, hasActiveAudioSession -> Bool in
|
||||||
return (hasActiveCalls || hasActiveGroupCalls) && hasActiveAudioSession
|
return (hasActiveCalls || hasActiveGroupCalls) && hasActiveAudioSession
|
||||||
@ -181,9 +187,9 @@ public final class SharedWakeupManager {
|
|||||||
|
|
||||||
let userInterfaceInUse = accountUserInterfaceInUse(account.id)
|
let userInterfaceInUse = accountUserInterfaceInUse(account.id)
|
||||||
|
|
||||||
return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager?.isPollingState(accountId: account.id) ?? .single(false), hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse)
|
return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager?.isPollingState(accountId: account.id) ?? .single(false), hasActiveAudio, keepUpdatesForCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse)
|
||||||
|> map { importantTasksRunning, isPollingState, hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse -> (Account, Bool, AccountTasks) in
|
|> map { importantTasksRunning, isPollingState, hasActiveAudio, keepUpdatesForCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse -> (Account, Bool, AccountTasks) in
|
||||||
return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: hasActiveLiveLocationPolling, backgroundDownloads: false, backgroundAudio: hasActiveAudio, activeCalls: hasActiveCalls, watchTasks: hasWatchTasks, userInterfaceInUse: userInterfaceInUse))
|
return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: hasActiveLiveLocationPolling, backgroundDownloads: false, backgroundAudio: hasActiveAudio, activeCalls: keepUpdatesForCalls, watchTasks: hasWatchTasks, userInterfaceInUse: userInterfaceInUse))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return combineLatest(signals)
|
return combineLatest(signals)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user