mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
Various improvements
This commit is contained in:
@@ -41,6 +41,193 @@ 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
|
||||
}
|
||||
@@ -55,7 +242,7 @@ public final class CallController: ViewController {
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let account: Account
|
||||
public let call: PresentationCall
|
||||
public let call: CallController.Call
|
||||
private let easyDebugAccess: Bool
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@@ -78,7 +265,10 @@ public final class CallController: ViewController {
|
||||
|
||||
public var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)?
|
||||
|
||||
public init(sharedContext: SharedAccountContext, account: Account, call: PresentationCall, easyDebugAccess: Bool) {
|
||||
public var onViewDidAppear: (() -> Void)?
|
||||
public var onViewDidDisappear: (() -> Void)?
|
||||
|
||||
public init(sharedContext: SharedAccountContext, account: Account, call: CallController.Call, easyDebugAccess: Bool) {
|
||||
self.sharedContext = sharedContext
|
||||
self.account = account
|
||||
self.call = call
|
||||
@@ -103,10 +293,84 @@ public final class CallController: ViewController {
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
|
||||
self.disposable = (call.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callState in
|
||||
self?.callStateUpdated(callState)
|
||||
})
|
||||
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.callMutedDisposable = (call.isMuted
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
@@ -148,26 +412,24 @@ public final class CallController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
var useV2 = true
|
||||
if let data = self.call.context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_callui_v2"] {
|
||||
useV2 = false
|
||||
}
|
||||
let displayNode = CallControllerNodeV2(
|
||||
sharedContext: self.sharedContext,
|
||||
account: self.account,
|
||||
presentationData: self.presentationData,
|
||||
statusBar: self.statusBar,
|
||||
debugInfo: self.call.debugInfo(),
|
||||
easyDebugAccess: self.easyDebugAccess,
|
||||
call: self.call
|
||||
)
|
||||
self.displayNode = displayNode
|
||||
self.isContentsReady.set(displayNode.isReady.get())
|
||||
|
||||
if !useV2 {
|
||||
self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
|
||||
self.isContentsReady.set(.single(true))
|
||||
} else {
|
||||
let displayNode = CallControllerNodeV2(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), easyDebugAccess: self.easyDebugAccess, call: self.call)
|
||||
self.displayNode = displayNode
|
||||
self.isContentsReady.set(displayNode.isReady.get())
|
||||
|
||||
displayNode.restoreUIForPictureInPicture = { [weak self] completion in
|
||||
guard let self, let restoreUIForPictureInPicture = self.restoreUIForPictureInPicture else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
restoreUIForPictureInPicture(completion)
|
||||
displayNode.restoreUIForPictureInPicture = { [weak self] completion in
|
||||
guard let self, let restoreUIForPictureInPicture = self.restoreUIForPictureInPicture else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
restoreUIForPictureInPicture(completion)
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
@@ -335,7 +597,11 @@ public final class CallController: ViewController {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
self.peerDisposable = (combineLatest(self.account.postbox.peerView(id: self.account.peerId) |> take(1), self.account.postbox.peerView(id: self.call.peerId), self.sharedContext.activeAccountsWithInfo |> take(1))
|
||||
self.peerDisposable = (combineLatest(queue: .mainQueue(),
|
||||
self.account.postbox.peerView(id: self.account.peerId) |> take(1),
|
||||
self.account.postbox.peerView(id: self.call.peerId),
|
||||
self.sharedContext.activeAccountsWithInfo |> take(1)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] accountView, view, activeAccountsWithInfo in
|
||||
if let strongSelf = self {
|
||||
if let accountPeer = accountView.peers[accountView.peerId], let peer = view.peers[view.peerId] {
|
||||
@@ -363,12 +629,16 @@ public final class CallController: ViewController {
|
||||
}
|
||||
|
||||
self.idleTimerExtensionDisposable.set(self.sharedContext.applicationBindings.pushIdleTimerExtension())
|
||||
|
||||
self.onViewDidAppear?()
|
||||
}
|
||||
|
||||
override public func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.idleTimerExtensionDisposable.set(nil)
|
||||
|
||||
self.onViewDidDisappear?()
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
Reference in New Issue
Block a user