mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
[WIP] Conference
This commit is contained in:
@@ -13,6 +13,7 @@ import AccountContext
|
||||
import TelegramNotices
|
||||
import AppBundle
|
||||
import TooltipUI
|
||||
import CallScreen
|
||||
|
||||
protocol CallControllerNodeProtocol: AnyObject {
|
||||
var isMuted: Bool { get set }
|
||||
@@ -41,193 +42,6 @@ protocol CallControllerNodeProtocol: AnyObject {
|
||||
}
|
||||
|
||||
public final class CallController: ViewController {
|
||||
public enum Call: Equatable {
|
||||
case call(PresentationCall)
|
||||
case groupCall(PresentationGroupCall)
|
||||
|
||||
public static func ==(lhs: Call, rhs: Call) -> Bool {
|
||||
switch lhs {
|
||||
case let .call(lhsCall):
|
||||
if case let .call(rhsCall) = rhs {
|
||||
return lhsCall === rhsCall
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupCall(lhsGroupCall):
|
||||
if case let .groupCall(rhsGroupCall) = rhs {
|
||||
return lhsGroupCall === rhsGroupCall
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var context: AccountContext {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.context
|
||||
case let .groupCall(groupCall):
|
||||
return groupCall.accountContext
|
||||
}
|
||||
}
|
||||
|
||||
public var peerId: EnginePeer.Id? {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.peerId
|
||||
case let .groupCall(groupCall):
|
||||
return groupCall.peerId
|
||||
}
|
||||
}
|
||||
|
||||
public func requestVideo() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.requestVideo()
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.requestVideo()
|
||||
}
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.disableVideo()
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.disableVideo()
|
||||
}
|
||||
}
|
||||
|
||||
public func disableScreencast() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
(call as? PresentationCallImpl)?.disableScreencast()
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.disableScreencast()
|
||||
}
|
||||
}
|
||||
|
||||
public func switchVideoCamera() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.switchVideoCamera()
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.switchVideoCamera()
|
||||
}
|
||||
}
|
||||
|
||||
public func toggleIsMuted() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.toggleIsMuted()
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.toggleIsMuted()
|
||||
}
|
||||
}
|
||||
|
||||
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.setCurrentAudioOutput(output)
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.setCurrentAudioOutput(output)
|
||||
}
|
||||
}
|
||||
|
||||
public var isMuted: Signal<Bool, NoError> {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.isMuted
|
||||
case let .groupCall(groupCall):
|
||||
return groupCall.isMuted
|
||||
}
|
||||
}
|
||||
|
||||
public var audioLevel: Signal<Float, NoError> {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.audioLevel
|
||||
case let .groupCall(groupCall):
|
||||
var audioLevelId: UInt32?
|
||||
return groupCall.audioLevels |> map { audioLevels -> Float in
|
||||
var result: Float = 0
|
||||
for item in audioLevels {
|
||||
if let audioLevelId {
|
||||
if item.1 == audioLevelId {
|
||||
result = item.2
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if item.1 != 0 {
|
||||
audioLevelId = item.1
|
||||
result = item.2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var isOutgoing: Bool {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.isOutgoing
|
||||
case .groupCall:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.makeOutgoingVideoView(completion: completion)
|
||||
case let .groupCall(groupCall):
|
||||
groupCall.makeOutgoingVideoView(requestClone: false, completion: { a, _ in
|
||||
completion(a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.audioOutputState
|
||||
case let .groupCall(groupCall):
|
||||
return groupCall.audioOutputState
|
||||
}
|
||||
}
|
||||
|
||||
public func debugInfo() -> Signal<(String, String), NoError> {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.debugInfo()
|
||||
case .groupCall:
|
||||
return .single(("", ""))
|
||||
}
|
||||
}
|
||||
|
||||
public func answer() {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
call.answer()
|
||||
case .groupCall:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func hangUp() -> Signal<Bool, NoError> {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.hangUp()
|
||||
case let .groupCall(groupCall):
|
||||
return groupCall.leave(terminateIfPossible: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var controllerNode: CallControllerNodeProtocol {
|
||||
return self.displayNode as! CallControllerNodeProtocol
|
||||
}
|
||||
@@ -242,7 +56,7 @@ public final class CallController: ViewController {
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let account: Account
|
||||
public let call: CallController.Call
|
||||
public let call: PresentationCall
|
||||
private let easyDebugAccess: Bool
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@@ -268,7 +82,7 @@ public final class CallController: ViewController {
|
||||
public var onViewDidAppear: (() -> Void)?
|
||||
public var onViewDidDisappear: (() -> Void)?
|
||||
|
||||
public init(sharedContext: SharedAccountContext, account: Account, call: CallController.Call, easyDebugAccess: Bool) {
|
||||
public init(sharedContext: SharedAccountContext, account: Account, call: PresentationCall, easyDebugAccess: Bool) {
|
||||
self.sharedContext = sharedContext
|
||||
self.account = account
|
||||
self.call = call
|
||||
@@ -293,84 +107,10 @@ public final class CallController: ViewController {
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
|
||||
switch call {
|
||||
case let .call(call):
|
||||
self.disposable = (call.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callState in
|
||||
self?.callStateUpdated(callState)
|
||||
})
|
||||
case let .groupCall(groupCall):
|
||||
let accountPeerId = groupCall.account.peerId
|
||||
let videoEndpoints: Signal<(local: String?, remote: PresentationGroupCallRequestedVideo?), NoError> = groupCall.members
|
||||
|> map { members -> (local: String?, remote: PresentationGroupCallRequestedVideo?) in
|
||||
guard let members else {
|
||||
return (nil, nil)
|
||||
}
|
||||
var local: String?
|
||||
var remote: PresentationGroupCallRequestedVideo?
|
||||
for participant in members.participants {
|
||||
if let video = participant.requestedPresentationVideoChannel(minQuality: .thumbnail, maxQuality: .full) ?? participant.requestedVideoChannel(minQuality: .thumbnail, maxQuality: .full) {
|
||||
if participant.peer.id == accountPeerId {
|
||||
local = video.endpointId
|
||||
} else {
|
||||
if remote == nil {
|
||||
remote = video
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (local, remote)
|
||||
}
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
return lhs == rhs
|
||||
})
|
||||
|
||||
var startTimestamp: Double?
|
||||
self.disposable = (combineLatest(queue: .mainQueue(),
|
||||
groupCall.state,
|
||||
videoEndpoints
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callState, videoEndpoints in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let mappedState: PresentationCallState.State
|
||||
switch callState.networkState {
|
||||
case .connecting:
|
||||
mappedState = .connecting(nil)
|
||||
case .connected:
|
||||
let timestamp = startTimestamp ?? CFAbsoluteTimeGetCurrent()
|
||||
startTimestamp = timestamp
|
||||
mappedState = .active(timestamp, nil, Data())
|
||||
}
|
||||
|
||||
var mappedLocalVideoState: PresentationCallState.VideoState = .inactive
|
||||
var mappedRemoteVideoState: PresentationCallState.RemoteVideoState = .inactive
|
||||
|
||||
if let local = videoEndpoints.local {
|
||||
mappedLocalVideoState = .active(isScreencast: false, endpointId: local)
|
||||
}
|
||||
if let remote = videoEndpoints.remote {
|
||||
mappedRemoteVideoState = .active(endpointId: remote.endpointId)
|
||||
}
|
||||
|
||||
if case let .groupCall(groupCall) = self.call {
|
||||
var requestedVideo: [PresentationGroupCallRequestedVideo] = []
|
||||
if let remote = videoEndpoints.remote {
|
||||
requestedVideo.append(remote)
|
||||
}
|
||||
groupCall.setRequestedVideoList(items: requestedVideo)
|
||||
}
|
||||
|
||||
self.callStateUpdated(PresentationCallState(
|
||||
state: mappedState,
|
||||
videoState: mappedLocalVideoState,
|
||||
remoteVideoState: mappedRemoteVideoState,
|
||||
remoteAudioState: .active,
|
||||
remoteBatteryLevel: .normal
|
||||
))
|
||||
})
|
||||
}
|
||||
self.disposable = (call.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callState in
|
||||
self?.callStateUpdated(callState)
|
||||
})
|
||||
|
||||
self.callMutedDisposable = (call.isMuted
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
@@ -605,11 +345,7 @@ public final class CallController: ViewController {
|
||||
}
|
||||
|
||||
let callPeerView: Signal<PeerView?, NoError>
|
||||
if let peerId = self.call.peerId {
|
||||
callPeerView = self.account.postbox.peerView(id: peerId) |> map(Optional.init)
|
||||
} else {
|
||||
callPeerView = .single(nil)
|
||||
}
|
||||
callPeerView = self.account.postbox.peerView(id: self.call.peerId) |> map(Optional.init)
|
||||
|
||||
self.peerDisposable = (combineLatest(queue: .mainQueue(),
|
||||
self.account.postbox.peerView(id: self.account.peerId) |> take(1),
|
||||
@@ -659,6 +395,26 @@ public final class CallController: ViewController {
|
||||
self.onViewDidDisappear?()
|
||||
}
|
||||
|
||||
final class AnimateOutToGroupChat {
|
||||
let incomingPeerId: EnginePeer.Id
|
||||
let incomingVideoLayer: CALayer?
|
||||
let incomingVideoPlaceholder: VideoSource.Output?
|
||||
|
||||
init(
|
||||
incomingPeerId: EnginePeer.Id,
|
||||
incomingVideoLayer: CALayer?,
|
||||
incomingVideoPlaceholder: VideoSource.Output?
|
||||
) {
|
||||
self.incomingPeerId = incomingPeerId
|
||||
self.incomingVideoLayer = incomingVideoLayer
|
||||
self.incomingVideoPlaceholder = incomingVideoPlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutToGroupChat(completion: @escaping () -> Void) -> AnimateOutToGroupChat? {
|
||||
return (self.controllerNode as? CallControllerNodeV2)?.animateOutToGroupChat(completion: completion)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
@@ -674,7 +430,17 @@ public final class CallController: ViewController {
|
||||
})
|
||||
}
|
||||
|
||||
public func dismissWithoutAnimation() {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
private func conferenceAddParticipant() {
|
||||
if "".isEmpty {
|
||||
let _ = self.call.upgradeToConference(completion: { _ in
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let controller = self.call.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(
|
||||
context: self.call.context,
|
||||
filter: [.onlyWriteable],
|
||||
@@ -690,17 +456,13 @@ public final class CallController: ViewController {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard case let .call(call) = self.call else {
|
||||
return
|
||||
}
|
||||
guard let call = call as? PresentationCallImpl else {
|
||||
guard let call = self.call as? PresentationCallImpl else {
|
||||
return
|
||||
}
|
||||
let _ = call.requestAddToConference(peerId: peer.id)
|
||||
}
|
||||
self.dismiss()
|
||||
|
||||
(self.call.context.sharedContext.mainWindow?.viewController as? NavigationController)?.pushViewController(controller)
|
||||
self.present(controller, in: .current)
|
||||
}
|
||||
|
||||
@objc private func backPressed() {
|
||||
|
||||
Reference in New Issue
Block a user