mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
[WIP] Conference calls
This commit is contained in:
parent
3e74304640
commit
962ca74101
15 changed files with 395 additions and 155 deletions
|
|
@ -414,6 +414,7 @@ public protocol PresentationGroupCall: AnyObject {
|
|||
|
||||
var isStream: Bool { get }
|
||||
var isConference: Bool { get }
|
||||
var conferenceSource: CallSessionInternalId? { get }
|
||||
var encryptionKeyValue: Data? { get }
|
||||
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||
|
||||
private let context: AccountContext
|
||||
private var theme: Theme
|
||||
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
|
||||
public private(set) var component: AnyComponent<ViewControllerComponentContainer.Environment>
|
||||
|
||||
private var presentationDataDisposable: Disposable?
|
||||
public private(set) var validLayout: ContainerViewLayout?
|
||||
|
|
@ -387,6 +387,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||
}
|
||||
|
||||
public func updateComponent(component: AnyComponent<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) {
|
||||
self.component = component
|
||||
self.node.updateComponent(component: component, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -811,7 +811,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
modalContainer.update(layout: modalContainer.isFlat ? globalOverlayLayout : layout, controllers: navigationLayout.modal[i].controllers, coveredByModalTransition: effectiveModalTransition, transition: containerTransition)
|
||||
|
||||
if modalContainer.supernode == nil && modalContainer.isReady {
|
||||
if let previousModalContainer = previousModalContainer {
|
||||
if let previousModalContainer {
|
||||
assert(previousModalContainer.supernode != nil)
|
||||
}
|
||||
|
||||
if let previousModalContainer, previousModalContainer.supernode != nil {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: previousModalContainer)
|
||||
} else if let inCallStatusBar = self.inCallStatusBar {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: inCallStatusBar)
|
||||
|
|
@ -1601,6 +1605,16 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
|
||||
public func setViewControllers(_ viewControllers: [UIViewController], animated: Bool, completion: @escaping () -> Void) {
|
||||
let requestedViewControllers = viewControllers
|
||||
var viewControllers: [UIViewController] = []
|
||||
for controller in requestedViewControllers {
|
||||
if !viewControllers.contains(where: { $0 === controller }) {
|
||||
viewControllers.append(controller)
|
||||
} else {
|
||||
assert(true)
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 ..< viewControllers.count {
|
||||
guard let controller = viewControllers[i] as? ViewController else {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ public final class CallKitIntegration {
|
|||
public func applyVoiceChatOutputMode(outputMode: AudioSessionOutputMode) {
|
||||
sharedProviderDelegate?.applyVoiceChatOutputMode(outputMode: outputMode)
|
||||
}
|
||||
|
||||
public func updateCallIsConference(uuid: UUID) {
|
||||
sharedProviderDelegate?.updateCallIsConference(uuid: uuid)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
||||
|
|
@ -276,6 +280,20 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||
self.provider.reportOutgoingCall(with: uuid, connectedAt: date)
|
||||
}
|
||||
|
||||
func updateCallIsConference(uuid: UUID) {
|
||||
let update = CXCallUpdate()
|
||||
let handle = CXHandle(type: .generic, value: "\(uuid)")
|
||||
update.remoteHandle = handle
|
||||
//TODO:localize
|
||||
update.localizedCallerName = "Group Call"
|
||||
update.supportsHolding = false
|
||||
update.supportsGrouping = false
|
||||
update.supportsUngrouping = false
|
||||
update.supportsDTMF = false
|
||||
|
||||
self.provider.reportCall(with: uuid, updated: update)
|
||||
}
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
Logger.shared.log("CallKitIntegration", "providerDidReset")
|
||||
|
||||
|
|
|
|||
|
|
@ -1038,6 +1038,9 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func updateCall(call: VideoChatCall) {
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
|
|
|
|||
|
|
@ -325,6 +325,8 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
private var localVideoEndpointId: String?
|
||||
private var remoteVideoEndpointId: String?
|
||||
|
||||
private var isMovedToConference: Bool = false
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
|
|
@ -541,6 +543,89 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
}
|
||||
|
||||
public func resetAsMovedToConference() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.isMovedToConference = true
|
||||
|
||||
self.sharedAudioContext = nil
|
||||
self.sessionState = nil
|
||||
self.callContextState = nil
|
||||
self.ongoingContext = nil
|
||||
self.ongoingContextStateDisposable?.dispose()
|
||||
self.ongoingContextStateDisposable = nil
|
||||
self.ongoingContextIsFailedDisposable?.dispose()
|
||||
self.ongoingContextIsFailedDisposable = nil
|
||||
self.ongoingContextIsDroppedDisposable?.dispose()
|
||||
self.ongoingContextIsDroppedDisposable = nil
|
||||
self.didDropCall = false
|
||||
self.requestedVideoAspect = nil
|
||||
self.reception = nil
|
||||
self.receptionDisposable?.dispose()
|
||||
self.receptionDisposable = nil
|
||||
self.audioLevelDisposable?.dispose()
|
||||
self.audioLevelDisposable = nil
|
||||
self.reportedIncomingCall = false
|
||||
self.batteryLevelDisposable?.dispose()
|
||||
self.batteryLevelDisposable = nil
|
||||
self.callWasActive = false
|
||||
self.shouldPresentCallRating = false
|
||||
self.previousVideoState = nil
|
||||
self.previousRemoteVideoState = nil
|
||||
self.previousRemoteAudioState = nil
|
||||
self.previousRemoteBatteryLevel = nil
|
||||
self.sessionStateDisposable?.dispose()
|
||||
self.sessionStateDisposable = nil
|
||||
self.activeTimestamp = nil
|
||||
self.audioSessionControl = nil
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.audioSessionDisposable = nil
|
||||
self.audioSessionShouldBeActiveDisposable?.dispose()
|
||||
self.audioSessionShouldBeActiveDisposable = nil
|
||||
self.audioSessionActiveDisposable?.dispose()
|
||||
self.audioSessionActiveDisposable = nil
|
||||
self.isAudioSessionActive = false
|
||||
self.currentTone = nil
|
||||
|
||||
self.dropCallKitCallTimer?.invalidate()
|
||||
self.dropCallKitCallTimer = nil
|
||||
|
||||
self.droppedCall = true
|
||||
|
||||
self.videoCapturer = nil
|
||||
|
||||
self.screencastBufferServerContext = nil
|
||||
self.screencastCapturer = nil
|
||||
self.isScreencastActive = false
|
||||
|
||||
if let proximityManagerIndex = self.proximityManagerIndex {
|
||||
DeviceProximityManager.shared().remove(proximityManagerIndex)
|
||||
self.proximityManagerIndex = nil
|
||||
}
|
||||
|
||||
self.screencastFramesDisposable.set(nil)
|
||||
self.screencastAudioDataDisposable.set(nil)
|
||||
self.screencastStateDisposable.set(nil)
|
||||
|
||||
self.conferenceCallImpl = nil
|
||||
|
||||
self.conferenceCallDisposable?.dispose()
|
||||
self.conferenceCallDisposable = nil
|
||||
|
||||
self.upgradedToConferenceCompletions.removeAll()
|
||||
|
||||
self.waitForConferenceCallReadyDisposable?.dispose()
|
||||
self.waitForConferenceCallReadyDisposable = nil
|
||||
|
||||
self.pendingInviteToConferencePeerIds.removeAll()
|
||||
|
||||
self.localVideoEndpointId = nil
|
||||
self.remoteVideoEndpointId = nil
|
||||
|
||||
self.callKitIntegration?.updateCallIsConference(uuid: self.internalId)
|
||||
}
|
||||
|
||||
func internal_markAsCanBeRemoved() {
|
||||
if !self.didSetCanBeRemoved {
|
||||
self.didSetCanBeRemoved = true
|
||||
|
|
@ -549,6 +634,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
private func updateSessionState(sessionState: CallSession, callContextState: OngoingCallContextState?, reception: Int32?, audioSessionControl: ManagedAudioSessionControl?) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.reception = reception
|
||||
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
|
|
@ -730,11 +818,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
case let .requesting(ringing, _):
|
||||
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
case let .active(_, _, keyVisualHash, _, _, _, _, _, _), let .switchedToConference(_, keyVisualHash, _):
|
||||
case let .active(_, _, keyVisualHash, _, _, _, _, _, _, _), let .switchedToConference(_, keyVisualHash, _):
|
||||
self.callWasActive = true
|
||||
|
||||
var isConference = false
|
||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall) = sessionState.state {
|
||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall, _) = sessionState.state {
|
||||
isConference = conferenceCall != nil
|
||||
} else if case .switchedToConference = sessionState.state {
|
||||
isConference = true
|
||||
|
|
@ -774,8 +862,8 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
var conferenceCallData: (key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)?
|
||||
var conferenceFromCallId: CallId?
|
||||
switch sessionState.state {
|
||||
case let .active(id, key, keyVisualHash, _, _, _, _, _, conferenceCall):
|
||||
if let conferenceCall {
|
||||
case let .active(id, key, keyVisualHash, _, _, _, _, _, conferenceCall, isIncomingConference):
|
||||
if let conferenceCall, !isIncomingConference {
|
||||
conferenceFromCallId = id
|
||||
conferenceCallData = (key, keyVisualHash, conferenceCall)
|
||||
}
|
||||
|
|
@ -789,6 +877,10 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
if self.conferenceCallDisposable == nil {
|
||||
self.conferenceCallDisposable = EmptyDisposable
|
||||
|
||||
#if DEBUG
|
||||
print("Switching to conference call with encryption key: \(key.base64EncodedString())")
|
||||
#endif
|
||||
|
||||
let conferenceCall = PresentationGroupCallImpl(
|
||||
accountContext: self.context,
|
||||
audioSession: self.audioSession,
|
||||
|
|
@ -810,6 +902,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
isStream: false,
|
||||
encryptionKey: (key, 1),
|
||||
conferenceFromCallId: conferenceFromCallId,
|
||||
conferenceSourceId: self.internalId,
|
||||
isConference: true,
|
||||
sharedAudioContext: self.sharedAudioContext
|
||||
)
|
||||
|
|
@ -917,7 +1010,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
if let _ = audioSessionControl {
|
||||
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)
|
||||
|
||||
if conferenceCallData != nil {
|
||||
|
|
@ -1044,7 +1137,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
var isConference = false
|
||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall) = sessionState.state {
|
||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall, _) = sessionState.state {
|
||||
isConference = conferenceCall != nil
|
||||
} else if case .switchedToConference = sessionState.state {
|
||||
isConference = true
|
||||
|
|
@ -1072,59 +1165,10 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
}
|
||||
|
||||
private func requestMediaChannelDescriptions(ssrcs: Set<UInt32>, completion: @escaping ([OngoingGroupCallContext.MediaChannelDescription]) -> Void) -> Disposable {
|
||||
/*func extractMediaChannelDescriptions(remainingSsrcs: inout Set<UInt32>, participants: [GroupCallParticipantsContext.Participant], into result: inout [OngoingGroupCallContext.MediaChannelDescription]) {
|
||||
for participant in participants {
|
||||
guard let audioSsrc = participant.ssrc else {
|
||||
continue
|
||||
}
|
||||
|
||||
if remainingSsrcs.contains(audioSsrc) {
|
||||
remainingSsrcs.remove(audioSsrc)
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
audioSsrc: audioSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
}
|
||||
|
||||
if let screencastSsrc = participant.presentationDescription?.audioSsrc {
|
||||
if remainingSsrcs.contains(screencastSsrc) {
|
||||
remainingSsrcs.remove(screencastSsrc)
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
audioSsrc: screencastSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var remainingSsrcs = ssrcs
|
||||
var result: [OngoingGroupCallContext.MediaChannelDescription] = []
|
||||
|
||||
if let membersValue = self.membersValue {
|
||||
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: membersValue.participants, into: &result)
|
||||
}
|
||||
|
||||
if !remainingSsrcs.isEmpty, let callInfo = self.internalState.callInfo {
|
||||
return (self.accountContext.engine.calls.getGroupCallParticipants(callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
|
||||
|> deliverOnMainQueue).start(next: { state in
|
||||
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: state.participants, into: &result)
|
||||
|
||||
completion(result)
|
||||
})
|
||||
} else {
|
||||
completion(result)
|
||||
return EmptyDisposable
|
||||
}*/
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
private func updateTone(_ state: PresentationCallState, callContextState: OngoingCallContextState?, previous: CallSession?) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
var tone: PresentationCallTone?
|
||||
if let callContextState = callContextState, case .reconnecting = callContextState.state {
|
||||
if !self.isVideo {
|
||||
|
|
@ -1174,16 +1218,25 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
private func updateIsAudioSessionActive(_ value: Bool) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if self.isAudioSessionActive != value {
|
||||
self.isAudioSessionActive = value
|
||||
}
|
||||
}
|
||||
|
||||
public func answer() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.answer(fromCallKitAction: false)
|
||||
}
|
||||
|
||||
func answer(fromCallKitAction: Bool) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
let (presentationData, present, openSettings) = self.getDeviceAccessData()
|
||||
|
||||
DeviceAccess.authorizeAccess(to: .microphone(.voiceCall), presentationData: presentationData, present: { c, a in
|
||||
|
|
@ -1234,6 +1287,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func hangUp() -> Signal<Bool, NoError> {
|
||||
if self.isMovedToConference {
|
||||
return .single(true)
|
||||
}
|
||||
let debugLogValue = Promise<String?>()
|
||||
self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get())
|
||||
self.ongoingContext?.stop(debugLogValue: debugLogValue)
|
||||
|
|
@ -1242,22 +1298,34 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func rejectBusy() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.callSessionManager.drop(internalId: self.internalId, reason: .busy, debugLog: .single(nil))
|
||||
let debugLog = Promise<String?>()
|
||||
self.ongoingContext?.stop(debugLogValue: debugLog)
|
||||
}
|
||||
|
||||
public func toggleIsMuted() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.setIsMuted(!self.isMutedValue)
|
||||
}
|
||||
|
||||
public func setIsMuted(_ value: Bool) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.isMutedValue = value
|
||||
self.isMutedPromise.set(self.isMutedValue)
|
||||
self.ongoingContext?.setIsMuted(self.isMutedValue)
|
||||
}
|
||||
|
||||
public func requestVideo() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
self.videoCapturer = videoCapturer
|
||||
|
|
@ -1270,6 +1338,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func requestVideo(capturer: OngoingCallVideoCapturer) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if self.videoCapturer == nil {
|
||||
self.videoCapturer = capturer
|
||||
}
|
||||
|
|
@ -1281,11 +1352,17 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func setRequestedVideoAspect(_ aspect: Float) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.requestedVideoAspect = aspect
|
||||
self.ongoingContext?.setRequestedVideoAspect(aspect)
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
|
|
@ -1295,6 +1372,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
private func resetScreencastContext() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
let basePath = self.context.sharedContext.basePath + "/broadcast-coordination"
|
||||
let screencastBufferServerContext = IpcGroupCallBufferAppContext(basePath: basePath)
|
||||
self.screencastBufferServerContext = screencastBufferServerContext
|
||||
|
|
@ -1332,6 +1412,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
private func requestScreencast() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.disableVideo()
|
||||
|
||||
if let screencastCapturer = self.screencastCapturer {
|
||||
|
|
@ -1343,6 +1426,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
func disableScreencast(reset: Bool = true) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if self.isScreencastActive {
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
|
|
@ -1357,10 +1443,16 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func setOutgoingVideoIsPaused(_ isPaused: Bool) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.videoCapturer?.setIsVideoEnabled(!isPaused)
|
||||
}
|
||||
|
||||
public func upgradeToConference(invitePeerIds: [EnginePeer.Id], completion: @escaping (PresentationGroupCall) -> Void) -> Disposable {
|
||||
if self.isMovedToConference {
|
||||
return EmptyDisposable
|
||||
}
|
||||
if let conferenceCall = self.conferenceCall {
|
||||
completion(conferenceCall)
|
||||
return EmptyDisposable
|
||||
|
|
@ -1385,6 +1477,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
if let sharedAudioContext = self.sharedAudioContext {
|
||||
sharedAudioContext.setCurrentAudioOutput(output)
|
||||
return
|
||||
|
|
@ -1416,6 +1511,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
func video(isIncoming: Bool) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError>? {
|
||||
if self.isMovedToConference {
|
||||
return nil
|
||||
}
|
||||
if isIncoming {
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
return ongoingContext.video(isIncoming: isIncoming)
|
||||
|
|
@ -1430,6 +1528,10 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
if self.isMovedToConference {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
self.videoCapturer = videoCapturer
|
||||
|
|
@ -1504,6 +1606,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
}
|
||||
|
||||
public func switchVideoCamera() {
|
||||
if self.isMovedToConference {
|
||||
return
|
||||
}
|
||||
self.useFrontCamera = !self.useFrontCamera
|
||||
self.videoCapturer?.switchVideoInput(isFront: self.useFrontCamera)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
private let removeCurrentCallDisposable = MetaDisposable()
|
||||
private let removeCurrentGroupCallDisposable = MetaDisposable()
|
||||
private var callToConferenceDisposable: Disposable?
|
||||
private var isConferenceReadyDisposable: Disposable?
|
||||
private var currentUpgradedToConferenceCallId: CallSessionInternalId?
|
||||
|
||||
private var currentGroupCallValue: VideoChatCall?
|
||||
|
|
@ -251,7 +252,19 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
}
|
||||
|
||||
endCallImpl = { [weak self] uuid in
|
||||
if let strongSelf = self, let currentCall = strongSelf.currentCall {
|
||||
guard let self else {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
if let currentGroupCall = self.currentGroupCall {
|
||||
switch currentGroupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
return conferenceSource.hangUp()
|
||||
case let .group(groupCall):
|
||||
return groupCall.leave(terminateIfPossible: false)
|
||||
}
|
||||
}
|
||||
if let currentCall = self.currentCall {
|
||||
return currentCall.hangUp()
|
||||
} else {
|
||||
return .single(false)
|
||||
|
|
@ -300,6 +313,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
self.proxyServerDisposable?.dispose()
|
||||
self.callSettingsDisposable?.dispose()
|
||||
self.callToConferenceDisposable?.dispose()
|
||||
self.isConferenceReadyDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
||||
|
|
@ -334,7 +348,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
internalId: firstState.2.id,
|
||||
peerId: firstState.2.peerId,
|
||||
isOutgoing: false,
|
||||
isIncomingConference: firstState.2.isConference,
|
||||
isIncomingConference: firstState.2.isIncomingConference,
|
||||
peer: EnginePeer(firstState.1),
|
||||
proxyServer: strongSelf.proxyServer,
|
||||
auxiliaryServers: [],
|
||||
|
|
@ -687,13 +701,21 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
}
|
||||
|
||||
if self.currentGroupCallValue != value {
|
||||
if case let .group(groupCall) = self.currentGroupCallValue, let conferenceSourceId = groupCall.conferenceSource {
|
||||
groupCall.accountContext.account.callSessionManager.drop(internalId: conferenceSourceId, reason: .hangUp, debugLog: .single(nil))
|
||||
(groupCall as! PresentationGroupCallImpl).callKitIntegration?.dropCall(uuid: conferenceSourceId)
|
||||
}
|
||||
|
||||
self.currentGroupCallValue = value
|
||||
|
||||
self.isConferenceReadyDisposable?.dispose()
|
||||
self.isConferenceReadyDisposable = nil
|
||||
|
||||
if let value {
|
||||
switch value {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
self.callToConferenceDisposable?.dispose()
|
||||
self.callToConferenceDisposable = (conferenceSource.conferenceState
|
||||
self.isConferenceReadyDisposable?.dispose()
|
||||
self.isConferenceReadyDisposable = (conferenceSource.conferenceState
|
||||
|> filter { value in
|
||||
if let value, case .ready = value {
|
||||
return true
|
||||
|
|
@ -709,6 +731,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
guard let groupCall = conferenceSource.conferenceCall else {
|
||||
return
|
||||
}
|
||||
(conferenceSource as! PresentationCallImpl).resetAsMovedToConference()
|
||||
self.updateCurrentGroupCall(.group(groupCall))
|
||||
})
|
||||
|
||||
|
|
@ -826,6 +849,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
isStream: false,
|
||||
encryptionKey: nil,
|
||||
conferenceFromCallId: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
sharedAudioContext: nil
|
||||
)
|
||||
|
|
@ -1052,6 +1076,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
isStream: initialCall.isStream ?? false,
|
||||
encryptionKey: nil,
|
||||
conferenceFromCallId: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
sharedAudioContext: nil
|
||||
)
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
public let account: Account
|
||||
public let accountContext: AccountContext
|
||||
private let audioSession: ManagedAudioSession
|
||||
private let callKitIntegration: CallKitIntegration?
|
||||
public let callKitIntegration: CallKitIntegration?
|
||||
public var isIntegratedWithCallKit: Bool {
|
||||
return self.callKitIntegration != nil
|
||||
}
|
||||
|
|
@ -1057,6 +1057,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
private let conferenceSourceId: CallSessionInternalId?
|
||||
public var conferenceSource: CallSessionInternalId? {
|
||||
return self.conferenceSourceId
|
||||
}
|
||||
|
||||
var internal_isRemoteConnected = Promise<Bool>()
|
||||
private var internal_isRemoteConnectedDisposable: Disposable?
|
||||
|
||||
|
|
@ -1081,6 +1086,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
isStream: Bool,
|
||||
encryptionKey: (key: Data, fingerprint: Int64)?,
|
||||
conferenceFromCallId: CallId?,
|
||||
conferenceSourceId: CallSessionInternalId?,
|
||||
isConference: Bool,
|
||||
sharedAudioContext: SharedCallAudioContext?
|
||||
) {
|
||||
|
|
@ -1109,6 +1115,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.hasScreencast = false
|
||||
self.isStream = isStream
|
||||
self.conferenceFromCallId = conferenceFromCallId
|
||||
self.conferenceSourceId = conferenceSourceId
|
||||
self.isConference = isConference
|
||||
self.encryptionKey = encryptionKey
|
||||
|
||||
|
|
|
|||
|
|
@ -326,6 +326,11 @@ final class VideoChatMicButtonComponent: Component {
|
|||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
if let previousComponent, previousComponent.call != component.call {
|
||||
self.audioLevelDisposable?.dispose()
|
||||
self.audioLevelDisposable = nil
|
||||
}
|
||||
|
||||
let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2)
|
||||
|
||||
let titleText: String
|
||||
|
|
|
|||
|
|
@ -230,6 +230,11 @@ final class VideoChatParticipantAvatarComponent: Component {
|
|||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
if let previousComponent, previousComponent.call != component.call {
|
||||
self.audioLevelDisposable?.dispose()
|
||||
self.audioLevelDisposable = nil
|
||||
}
|
||||
|
||||
let avatarNode: AvatarNode
|
||||
if let current = self.avatarNode {
|
||||
avatarNode = current
|
||||
|
|
|
|||
|
|
@ -163,6 +163,16 @@ final class VideoChatScreenComponent: Component {
|
|||
}
|
||||
|
||||
static func ==(lhs: VideoChatScreenComponent, rhs: VideoChatScreenComponent) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if lhs.initialData !== rhs.initialData {
|
||||
return false
|
||||
}
|
||||
if lhs.initialCall != rhs.initialCall {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -1082,7 +1092,12 @@ final class VideoChatScreenComponent: Component {
|
|||
self.callState = component.initialData.callState
|
||||
}
|
||||
|
||||
var call = self.currentCall ?? component.initialCall
|
||||
var call: VideoChatCall
|
||||
if let previousComponent = self.component, previousComponent.initialCall != component.initialCall {
|
||||
call = component.initialCall
|
||||
} else {
|
||||
call = self.currentCall ?? component.initialCall
|
||||
}
|
||||
if case let .conferenceSource(conferenceSource) = call, let conferenceCall = conferenceSource.conferenceCall, conferenceSource.conferenceStateValue == .ready {
|
||||
call = .group(conferenceCall)
|
||||
}
|
||||
|
|
@ -2517,6 +2532,17 @@ final class VideoChatScreenV2Impl: ViewControllerComponentContainer, VoiceChatCo
|
|||
self.idleTimerExtensionDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateCall(call: VideoChatCall) {
|
||||
self.call = call
|
||||
if let component = self.component.wrapped as? VideoChatScreenComponent {
|
||||
// This is only to clear the reference to regular call
|
||||
self.updateComponent(component: AnyComponent(VideoChatScreenComponent(
|
||||
initialData: component.initialData,
|
||||
initialCall: call
|
||||
)), transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,8 @@ public protocol VoiceChatController: ViewController {
|
|||
var onViewDidAppear: (() -> Void)? { get set }
|
||||
var onViewDidDisappear: (() -> Void)? { get set }
|
||||
|
||||
func updateCall(call: VideoChatCall)
|
||||
|
||||
func dismiss(closing: Bool, manual: Bool)
|
||||
}
|
||||
|
||||
|
|
@ -6938,6 +6940,9 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
|||
}
|
||||
}
|
||||
|
||||
func updateCall(call: VideoChatCall) {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(controller: self, sharedContext: self.sharedContext, call: self.callImpl)
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ enum CallSessionInternalState {
|
|||
case requesting(a: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?, conferenceCall: GroupCallReference?)
|
||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, conferenceCall: GroupCallReference?, 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, conferenceCall: GroupCallReference?)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||
|
|
@ -98,7 +98,7 @@ enum CallSessionInternalState {
|
|||
return id
|
||||
case let .confirming(id, _, _, _, _, _, _):
|
||||
return id
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case .switchedToConference:
|
||||
return nil
|
||||
|
|
@ -137,7 +137,7 @@ public struct CallSessionRingingState: Equatable {
|
|||
public let peerId: PeerId
|
||||
public let isVideo: Bool
|
||||
public let isVideoPossible: Bool
|
||||
public let isConference: Bool
|
||||
public let isIncomingConference: Bool
|
||||
}
|
||||
|
||||
public enum DropCallReason {
|
||||
|
|
@ -166,7 +166,7 @@ public enum CallSessionState {
|
|||
case ringing
|
||||
case accepting
|
||||
case requesting(ringing: Bool, conferenceCall: GroupCallReference?)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
case dropping(reason: CallSessionTerminationReason)
|
||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||
|
|
@ -183,8 +183,8 @@ public enum CallSessionState {
|
|||
self = .requesting(ringing: true, conferenceCall: conferenceCall)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp, conferenceCall):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil, conferenceCall: conferenceCall)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall, willSwitchToConference):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall, willSwitchToConference: willSwitchToConference)
|
||||
case let .dropping(reason, _):
|
||||
self = .dropping(reason: reason)
|
||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||
|
|
@ -335,7 +335,7 @@ private func parseConnectionSet(primary: Api.PhoneConnection, alternative: [Api.
|
|||
private final class CallSessionContext {
|
||||
let peerId: PeerId
|
||||
let isOutgoing: Bool
|
||||
let isConference: Bool
|
||||
let isIncomingConference: Bool
|
||||
var type: CallSession.CallType
|
||||
var isVideoPossible: Bool
|
||||
let pendingConference: (conference: GroupCallReference, encryptionKey: Data)?
|
||||
|
|
@ -355,10 +355,10 @@ private final class CallSessionContext {
|
|||
}
|
||||
}
|
||||
|
||||
init(peerId: PeerId, isOutgoing: Bool, isConference: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
|
||||
init(peerId: PeerId, isOutgoing: Bool, isIncomingConference: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
|
||||
self.peerId = peerId
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isConference = isConference
|
||||
self.isIncomingConference = isIncomingConference
|
||||
self.type = type
|
||||
self.isVideoPossible = isVideoPossible
|
||||
self.pendingConference = pendingConference
|
||||
|
|
@ -555,7 +555,7 @@ private final class CallSessionManagerContext {
|
|||
peerId: context.peerId,
|
||||
isVideo: context.type == .video,
|
||||
isVideoPossible: context.isVideoPossible,
|
||||
isConference: context.isConference
|
||||
isIncomingConference: context.isIncomingConference
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -599,7 +599,7 @@ private final class CallSessionManagerContext {
|
|||
//#endif
|
||||
|
||||
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, isConference: conferenceCall != nil, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, isIncomingConference: conferenceCall != nil, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
|
||||
self.contexts[internalId] = context
|
||||
let queue = self.queue
|
||||
|
||||
|
|
@ -653,7 +653,7 @@ private final class CallSessionManagerContext {
|
|||
case let .accepting(id, accessHash, _, _, _, disposable):
|
||||
dropData = (id, accessHash, .abort)
|
||||
disposable.dispose()
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _, _):
|
||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
|
|
@ -755,7 +755,7 @@ private final class CallSessionManagerContext {
|
|||
var dropData: (CallSessionStableId, Int64)?
|
||||
let isVideo = context.type == .video
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _, _):
|
||||
dropData = (id, accessHash)
|
||||
default:
|
||||
break
|
||||
|
|
@ -807,7 +807,7 @@ private final class CallSessionManagerContext {
|
|||
strongSelf.contextUpdated(internalId: internalId)
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
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, conferenceCall: conferenceCall)
|
||||
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, conferenceCall: conferenceCall, willSwitchToConference: context.isIncomingConference)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
|
|
@ -828,7 +828,7 @@ private final class CallSessionManagerContext {
|
|||
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
||||
if let context = self.contexts[internalId] {
|
||||
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())
|
||||
default:
|
||||
break
|
||||
|
|
@ -844,7 +844,7 @@ private final class CallSessionManagerContext {
|
|||
|
||||
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
||||
if conferenceCall != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -864,8 +864,8 @@ private final class CallSessionManagerContext {
|
|||
}
|
||||
if let result {
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, 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, conferenceCall: result)
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, 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, conferenceCall: result, willSwitchToConference: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
default:
|
||||
break
|
||||
|
|
@ -977,7 +977,7 @@ private final class CallSessionManagerContext {
|
|||
disposable.dispose()
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
||||
if let conferenceCall, case let .phoneCallDiscardReasonAllowGroupCall(encryptedGroupKey) = reason {
|
||||
context.state = .switchedToConference(key: encryptedGroupKey.makeData(), keyVisualHash: MTSha256(encryptedGroupKey.makeData()), conferenceCall: conferenceCall)
|
||||
} else {
|
||||
|
|
@ -1016,8 +1016,8 @@ private final class CallSessionManagerContext {
|
|||
switch context.state {
|
||||
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
||||
break
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, 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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, 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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||
|
|
@ -1036,7 +1036,7 @@ private final class CallSessionManagerContext {
|
|||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
|
|
@ -1063,7 +1063,7 @@ private final class CallSessionManagerContext {
|
|||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
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, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
|
|
@ -1173,7 +1173,7 @@ private final class CallSessionManagerContext {
|
|||
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
|
||||
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
|
||||
if randomStatus == 0 {
|
||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, isConference: conferenceCall != nil, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, pendingConference: conferenceCall, state: .requesting(a: a, conferenceCall: conferenceCall?.conference, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo, conferenceCall: conferenceCall?.conference) |> deliverOn(queue)).start(next: { [weak self] result in
|
||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, isIncomingConference: false, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, pendingConference: conferenceCall, state: .requesting(a: a, conferenceCall: conferenceCall?.conference, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo, conferenceCall: conferenceCall?.conference) |> deliverOn(queue)).start(next: { [weak self] result in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||
if case .requesting = context.state {
|
||||
switch result {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
public var currentGroupCallController: ViewController? {
|
||||
return self.groupCallController
|
||||
}
|
||||
private var hasGroupCallOnScreenValue: Bool = false
|
||||
private let hasGroupCallOnScreenPromise = Promise<Bool>(false)
|
||||
public var hasGroupCallOnScreen: Signal<Bool, NoError> {
|
||||
return self.hasGroupCallOnScreenPromise.get()
|
||||
|
|
@ -1182,15 +1183,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
|
||||
self.notificationController?.setBlocking(nil)
|
||||
}
|
||||
|
||||
self.callStateDisposable?.dispose()
|
||||
self.callStateDisposable = nil
|
||||
}
|
||||
|
||||
self.currentCall = call
|
||||
|
||||
let beginDisplayingCallStatusBar = Promise<Void>()
|
||||
|
||||
var shouldResetGroupCallOnScreen = true
|
||||
|
||||
var transitioningToConferenceCallController: CallController?
|
||||
if let call, case let .group(groupCall) = call, case let .conferenceSource(conferenceSource) = groupCall, let callController = self.callController, callController.call === conferenceSource {
|
||||
transitioningToConferenceCallController = callController
|
||||
|
|
@ -1198,8 +1198,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
callController.dismissWithoutAnimation()
|
||||
}
|
||||
self.callController = nil
|
||||
} else {
|
||||
self.hasGroupCallOnScreenPromise.set(.single(false))
|
||||
shouldResetGroupCallOnScreen = false
|
||||
}
|
||||
|
||||
if let callController = self.callController {
|
||||
|
|
@ -1207,10 +1206,27 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
callController.dismiss()
|
||||
}
|
||||
if let groupCallController = self.groupCallController {
|
||||
if case let .group(groupCall) = call, case let .group(groupCall) = groupCall, let conferenceSourceId = groupCall.conferenceSource {
|
||||
if case let .conferenceSource(conferenceSource) = groupCallController.call, conferenceSource.internalId == conferenceSourceId {
|
||||
groupCallController.updateCall(call: .group(groupCall))
|
||||
|
||||
self.updateInCallStatusBarData(hasGroupCallOnScreen: self.hasGroupCallOnScreenValue)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
self.groupCallController = nil
|
||||
groupCallController.dismiss()
|
||||
}
|
||||
|
||||
if shouldResetGroupCallOnScreen {
|
||||
self.hasGroupCallOnScreenPromise.set(.single(false))
|
||||
}
|
||||
|
||||
self.callStateDisposable?.dispose()
|
||||
self.callStateDisposable = nil
|
||||
|
||||
if case let .call(call) = call {
|
||||
let callController = CallController(sharedContext: self, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
self.callController = callController
|
||||
|
|
@ -1282,36 +1298,36 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.awaitingCallConnectionDisposable = (call.state
|
||||
|> filter { state in
|
||||
switch state.state {
|
||||
case .ringing:
|
||||
return false
|
||||
case .terminating, .terminated:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak callController] _ in
|
||||
guard let self, let callController, self.callController === callController else {
|
||||
return
|
||||
}
|
||||
self.notificationController?.setBlocking(nil)
|
||||
|
||||
self.callPeerDisposable?.dispose()
|
||||
self.callPeerDisposable = nil
|
||||
|
||||
thisCallIsOnScreenPromise.set(true)
|
||||
if useFlatModalCallsPresentation(context: callController.call.context) {
|
||||
(self.mainWindow?.viewController as? NavigationController)?.pushViewController(callController)
|
||||
} else {
|
||||
self.mainWindow?.present(callController, on: .calls)
|
||||
self.awaitingCallConnectionDisposable = (call.state
|
||||
|> filter { state in
|
||||
switch state.state {
|
||||
case .ringing:
|
||||
return false
|
||||
case .terminating, .terminated:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak callController] _ in
|
||||
guard let self, let callController, self.callController === callController else {
|
||||
return
|
||||
}
|
||||
self.notificationController?.setBlocking(nil)
|
||||
|
||||
self.callPeerDisposable?.dispose()
|
||||
self.callPeerDisposable = nil
|
||||
|
||||
thisCallIsOnScreenPromise.set(true)
|
||||
if useFlatModalCallsPresentation(context: callController.call.context) {
|
||||
(self.mainWindow?.viewController as? NavigationController)?.pushViewController(callController)
|
||||
} else {
|
||||
self.mainWindow?.present(callController, on: .calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
beginDisplayingCallStatusBar.set(call.state
|
||||
|> filter { state in
|
||||
|
|
@ -1389,40 +1405,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
var statusBarContent: CallStatusBarNodeImpl.Content?
|
||||
if !hasGroupCallOnScreen, let currentCall = self.currentCall {
|
||||
switch currentCall {
|
||||
case let .call(call):
|
||||
statusBarContent = .call(self, call.context.account, call)
|
||||
case let .group(groupCall):
|
||||
switch groupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
statusBarContent = .call(self, conferenceSource.context.account, conferenceSource)
|
||||
case let .group(groupCall):
|
||||
statusBarContent = .groupCall(self, groupCall.account, groupCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedCallStatusBarNode: CallStatusBarNodeImpl?
|
||||
if let statusBarContent {
|
||||
if let current = self.currentCallStatusBarNode {
|
||||
resolvedCallStatusBarNode = current
|
||||
} else {
|
||||
resolvedCallStatusBarNode = CallStatusBarNodeImpl()
|
||||
self.currentCallStatusBarNode = resolvedCallStatusBarNode
|
||||
}
|
||||
resolvedCallStatusBarNode?.update(content: statusBarContent)
|
||||
} else {
|
||||
self.currentCallStatusBarNode = nil
|
||||
}
|
||||
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.setForceInCallStatusBar(resolvedCallStatusBarNode)
|
||||
}
|
||||
self.hasGroupCallOnScreenValue = hasGroupCallOnScreen
|
||||
self.updateInCallStatusBarData(hasGroupCallOnScreen: hasGroupCallOnScreen)
|
||||
})
|
||||
} else {
|
||||
self.hasGroupCallOnScreenValue = false
|
||||
self.currentCallStatusBarNode = nil
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.setForceInCallStatusBar(nil)
|
||||
|
|
@ -1430,6 +1417,40 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
}
|
||||
}
|
||||
|
||||
private func updateInCallStatusBarData(hasGroupCallOnScreen: Bool) {
|
||||
var statusBarContent: CallStatusBarNodeImpl.Content?
|
||||
if !hasGroupCallOnScreen, let currentCall = self.currentCall {
|
||||
switch currentCall {
|
||||
case let .call(call):
|
||||
statusBarContent = .call(self, call.context.account, call)
|
||||
case let .group(groupCall):
|
||||
switch groupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
statusBarContent = .call(self, conferenceSource.context.account, conferenceSource)
|
||||
case let .group(groupCall):
|
||||
statusBarContent = .groupCall(self, groupCall.account, groupCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedCallStatusBarNode: CallStatusBarNodeImpl?
|
||||
if let statusBarContent {
|
||||
if let current = self.currentCallStatusBarNode {
|
||||
resolvedCallStatusBarNode = current
|
||||
} else {
|
||||
resolvedCallStatusBarNode = CallStatusBarNodeImpl()
|
||||
self.currentCallStatusBarNode = resolvedCallStatusBarNode
|
||||
}
|
||||
resolvedCallStatusBarNode?.update(content: statusBarContent)
|
||||
} else {
|
||||
self.currentCallStatusBarNode = nil
|
||||
}
|
||||
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.setForceInCallStatusBar(resolvedCallStatusBarNode)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentControllerWithCurrentCall() {
|
||||
/*guard let call = self.call else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -2275,6 +2275,10 @@ isConference:(bool)isConference {
|
|||
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
|
||||
memcpy(encryptionKeyValue->data(), encryptionKey.bytes, encryptionKey.length);
|
||||
|
||||
#if DEBUG
|
||||
NSLog(@"Encryption key: %@", [encryptionKey base64EncodedStringWithOptions:0]);
|
||||
#endif
|
||||
|
||||
mappedEncryptionKey = tgcalls::EncryptionKey(encryptionKeyValue, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue