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:
@@ -57,14 +57,14 @@ public struct PresentationCallState: Equatable {
|
|||||||
public enum VideoState: Equatable {
|
public enum VideoState: Equatable {
|
||||||
case notAvailable
|
case notAvailable
|
||||||
case inactive
|
case inactive
|
||||||
case active(isScreencast: Bool)
|
case active(isScreencast: Bool, endpointId: String)
|
||||||
case paused(isScreencast: Bool)
|
case paused(isScreencast: Bool, endpointId: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RemoteVideoState: Equatable {
|
public enum RemoteVideoState: Equatable {
|
||||||
case inactive
|
case inactive
|
||||||
case active
|
case active(endpointId: String)
|
||||||
case paused
|
case paused(endpointId: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RemoteAudioState: Equatable {
|
public enum RemoteAudioState: Equatable {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case disableReloginTokens(Bool)
|
case disableReloginTokens(Bool)
|
||||||
case disableCallV2(Bool)
|
case disableCallV2(Bool)
|
||||||
case experimentalCallMute(Bool)
|
case experimentalCallMute(Bool)
|
||||||
case autoBenchmarkReflectors(Bool)
|
case conferenceCalls(Bool)
|
||||||
case benchmarkReflectors
|
case benchmarkReflectors
|
||||||
case enableLocalTranslation(Bool)
|
case enableLocalTranslation(Bool)
|
||||||
case preferredVideoCodec(Int, String, String?, Bool)
|
case preferredVideoCodec(Int, String, String?, Bool)
|
||||||
@@ -132,7 +132,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.web.rawValue
|
return DebugControllerSection.web.rawValue
|
||||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .disableCallV2, .experimentalCallMute, .autoBenchmarkReflectors, .benchmarkReflectors, .enableLocalTranslation:
|
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .disableCallV2, .experimentalCallMute, .conferenceCalls, .benchmarkReflectors, .enableLocalTranslation:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .logTranslationRecognition, .resetTranslationStates:
|
case .logTranslationRecognition, .resetTranslationStates:
|
||||||
return DebugControllerSection.translation.rawValue
|
return DebugControllerSection.translation.rawValue
|
||||||
@@ -249,7 +249,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 51
|
return 51
|
||||||
case .experimentalCallMute:
|
case .experimentalCallMute:
|
||||||
return 52
|
return 52
|
||||||
case .autoBenchmarkReflectors:
|
case .conferenceCalls:
|
||||||
return 53
|
return 53
|
||||||
case .benchmarkReflectors:
|
case .benchmarkReflectors:
|
||||||
return 54
|
return 54
|
||||||
@@ -1345,12 +1345,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .autoBenchmarkReflectors(value):
|
case let .conferenceCalls(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "Auto-Benchmark Reflectors [Restart]", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Conference [WIP]", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||||
settings.autoBenchmarkReflectors = value
|
settings.conferenceCalls = value
|
||||||
return PreferencesEntry(settings)
|
return PreferencesEntry(settings)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
@@ -1577,11 +1577,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.disableCallV2(experimentalSettings.disableCallV2))
|
entries.append(.disableCallV2(experimentalSettings.disableCallV2))
|
||||||
entries.append(.experimentalCallMute(experimentalSettings.experimentalCallMute))
|
entries.append(.experimentalCallMute(experimentalSettings.experimentalCallMute))
|
||||||
|
|
||||||
var defaultAutoBenchmarkReflectors = false
|
entries.append(.conferenceCalls(experimentalSettings.conferenceCalls))
|
||||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
|
||||||
defaultAutoBenchmarkReflectors = true
|
|
||||||
}
|
|
||||||
entries.append(.autoBenchmarkReflectors(experimentalSettings.autoBenchmarkReflectors ?? defaultAutoBenchmarkReflectors))
|
|
||||||
|
|
||||||
entries.append(.benchmarkReflectors)
|
entries.append(.benchmarkReflectors)
|
||||||
entries.append(.enableLocalTranslation(experimentalSettings.enableLocalTranslation))
|
entries.append(.enableLocalTranslation(experimentalSettings.enableLocalTranslation))
|
||||||
|
|||||||
@@ -41,6 +41,193 @@ protocol CallControllerNodeProtocol: AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class CallController: ViewController {
|
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 {
|
private var controllerNode: CallControllerNodeProtocol {
|
||||||
return self.displayNode as! CallControllerNodeProtocol
|
return self.displayNode as! CallControllerNodeProtocol
|
||||||
}
|
}
|
||||||
@@ -55,7 +242,7 @@ public final class CallController: ViewController {
|
|||||||
|
|
||||||
private let sharedContext: SharedAccountContext
|
private let sharedContext: SharedAccountContext
|
||||||
private let account: Account
|
private let account: Account
|
||||||
public let call: PresentationCall
|
public let call: CallController.Call
|
||||||
private let easyDebugAccess: Bool
|
private let easyDebugAccess: Bool
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
@@ -78,7 +265,10 @@ public final class CallController: ViewController {
|
|||||||
|
|
||||||
public var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)?
|
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.sharedContext = sharedContext
|
||||||
self.account = account
|
self.account = account
|
||||||
self.call = call
|
self.call = call
|
||||||
@@ -103,10 +293,84 @@ public final class CallController: ViewController {
|
|||||||
|
|
||||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||||
|
|
||||||
|
switch call {
|
||||||
|
case let .call(call):
|
||||||
self.disposable = (call.state
|
self.disposable = (call.state
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] callState in
|
|> deliverOnMainQueue).start(next: { [weak self] callState in
|
||||||
self?.callStateUpdated(callState)
|
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
|
self.callMutedDisposable = (call.isMuted
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
@@ -148,16 +412,15 @@ public final class CallController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
var useV2 = true
|
let displayNode = CallControllerNodeV2(
|
||||||
if let data = self.call.context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_callui_v2"] {
|
sharedContext: self.sharedContext,
|
||||||
useV2 = false
|
account: self.account,
|
||||||
}
|
presentationData: self.presentationData,
|
||||||
|
statusBar: self.statusBar,
|
||||||
if !useV2 {
|
debugInfo: self.call.debugInfo(),
|
||||||
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)
|
easyDebugAccess: self.easyDebugAccess,
|
||||||
self.isContentsReady.set(.single(true))
|
call: self.call
|
||||||
} 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.displayNode = displayNode
|
||||||
self.isContentsReady.set(displayNode.isReady.get())
|
self.isContentsReady.set(displayNode.isReady.get())
|
||||||
|
|
||||||
@@ -168,7 +431,6 @@ public final class CallController: ViewController {
|
|||||||
}
|
}
|
||||||
restoreUIForPictureInPicture(completion)
|
restoreUIForPictureInPicture(completion)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
self.controllerNode.toggleMute = { [weak self] in
|
self.controllerNode.toggleMute = { [weak self] in
|
||||||
@@ -335,7 +597,11 @@ public final class CallController: ViewController {
|
|||||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] accountView, view, activeAccountsWithInfo in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let accountPeer = accountView.peers[accountView.peerId], let peer = view.peers[view.peerId] {
|
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.idleTimerExtensionDisposable.set(self.sharedContext.applicationBindings.pushIdleTimerExtension())
|
||||||
|
|
||||||
|
self.onViewDidAppear?()
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func viewDidDisappear(_ animated: Bool) {
|
override public func viewDidDisappear(_ animated: Bool) {
|
||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
self.idleTimerExtensionDisposable.set(nil)
|
self.idleTimerExtensionDisposable.set(nil)
|
||||||
|
|
||||||
|
self.onViewDidDisappear?()
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
private let account: Account
|
private let account: Account
|
||||||
private let presentationData: PresentationData
|
private let presentationData: PresentationData
|
||||||
private let statusBar: StatusBar
|
private let statusBar: StatusBar
|
||||||
private let call: PresentationCall
|
private let call: CallController.Call
|
||||||
|
|
||||||
private let containerView: UIView
|
private let containerView: UIView
|
||||||
private let callScreen: PrivateCallScreen
|
private let callScreen: PrivateCallScreen
|
||||||
@@ -90,7 +90,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
statusBar: StatusBar,
|
statusBar: StatusBar,
|
||||||
debugInfo: Signal<(String, String), NoError>,
|
debugInfo: Signal<(String, String), NoError>,
|
||||||
easyDebugAccess: Bool,
|
easyDebugAccess: Bool,
|
||||||
call: PresentationCall
|
call: CallController.Call
|
||||||
) {
|
) {
|
||||||
self.sharedContext = sharedContext
|
self.sharedContext = sharedContext
|
||||||
self.account = account
|
self.account = account
|
||||||
@@ -172,9 +172,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
isRemoteBatteryLow: false,
|
isRemoteBatteryLow: false,
|
||||||
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency
|
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency
|
||||||
)
|
)
|
||||||
if let peer = call.peer {
|
|
||||||
self.updatePeer(peer: peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.isMicrophoneMutedDisposable = (call.isMuted
|
self.isMicrophoneMutedDisposable = (call.isMuted
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] isMuted in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] isMuted in
|
||||||
@@ -308,9 +305,9 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
switch callState.state {
|
switch callState.state {
|
||||||
case .active:
|
case .active:
|
||||||
switch callState.videoState {
|
switch callState.videoState {
|
||||||
case .active(let isScreencast), .paused(let isScreencast):
|
case .active(let isScreencast, _), .paused(let isScreencast, _):
|
||||||
if isScreencast {
|
if isScreencast {
|
||||||
(self.call as? PresentationCallImpl)?.disableScreencast()
|
self.call.disableScreencast()
|
||||||
} else {
|
} else {
|
||||||
self.call.disableVideo()
|
self.call.disableVideo()
|
||||||
}
|
}
|
||||||
@@ -489,23 +486,45 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
self.remoteVideo = nil
|
self.remoteVideo = nil
|
||||||
default:
|
default:
|
||||||
switch callState.videoState {
|
switch callState.videoState {
|
||||||
case .active(let isScreencast), .paused(let isScreencast):
|
case .active(let isScreencast, let endpointId), .paused(let isScreencast, let endpointId):
|
||||||
if isScreencast {
|
if isScreencast {
|
||||||
self.localVideo = nil
|
self.localVideo = nil
|
||||||
} else {
|
} else {
|
||||||
if self.localVideo == nil, let call = self.call as? PresentationCallImpl, let videoStreamSignal = call.video(isIncoming: false) {
|
if self.localVideo == nil {
|
||||||
|
switch self.call {
|
||||||
|
case let .call(call):
|
||||||
|
if let call = call as? PresentationCallImpl, let videoStreamSignal = call.video(isIncoming: false) {
|
||||||
self.localVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
self.localVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
||||||
}
|
}
|
||||||
|
case let .groupCall(groupCall):
|
||||||
|
if let groupCall = groupCall as? PresentationGroupCallImpl {
|
||||||
|
if let videoStreamSignal = groupCall.video(endpointId: endpointId) {
|
||||||
|
self.localVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case .inactive, .notAvailable:
|
case .inactive, .notAvailable:
|
||||||
self.localVideo = nil
|
self.localVideo = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch callState.remoteVideoState {
|
switch callState.remoteVideoState {
|
||||||
case .active, .paused:
|
case .active(let endpointId), .paused(let endpointId):
|
||||||
if self.remoteVideo == nil, let call = self.call as? PresentationCallImpl, let videoStreamSignal = call.video(isIncoming: true) {
|
if self.remoteVideo == nil {
|
||||||
|
switch self.call {
|
||||||
|
case let .call(call):
|
||||||
|
if let call = call as? PresentationCallImpl, let videoStreamSignal = call.video(isIncoming: true) {
|
||||||
self.remoteVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
self.remoteVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
||||||
}
|
}
|
||||||
|
case let .groupCall(groupCall):
|
||||||
|
if let groupCall = groupCall as? PresentationGroupCallImpl {
|
||||||
|
if let videoStreamSignal = groupCall.video(endpointId: endpointId) {
|
||||||
|
self.remoteVideo = AdaptedCallVideoSource(videoStreamSignal: videoStreamSignal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
case .inactive:
|
case .inactive:
|
||||||
self.remoteVideo = nil
|
self.remoteVideo = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ 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), 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))
|
||||||
} 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))
|
||||||
}
|
}
|
||||||
@@ -418,19 +418,19 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
case .notAvailable:
|
case .notAvailable:
|
||||||
mappedVideoState = .notAvailable
|
mappedVideoState = .notAvailable
|
||||||
case .active:
|
case .active:
|
||||||
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
mappedVideoState = .active(isScreencast: self.isScreencastActive, endpointId: "")
|
||||||
case .inactive:
|
case .inactive:
|
||||||
mappedVideoState = .inactive
|
mappedVideoState = .inactive
|
||||||
case .paused:
|
case .paused:
|
||||||
mappedVideoState = .paused(isScreencast: self.isScreencastActive)
|
mappedVideoState = .paused(isScreencast: self.isScreencastActive, endpointId: "")
|
||||||
}
|
}
|
||||||
switch callContextState.remoteVideoState {
|
switch callContextState.remoteVideoState {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
mappedRemoteVideoState = .inactive
|
mappedRemoteVideoState = .inactive
|
||||||
case .active:
|
case .active:
|
||||||
mappedRemoteVideoState = .active
|
mappedRemoteVideoState = .active(endpointId: "")
|
||||||
case .paused:
|
case .paused:
|
||||||
mappedRemoteVideoState = .paused
|
mappedRemoteVideoState = .paused(endpointId: "")
|
||||||
}
|
}
|
||||||
switch callContextState.remoteAudioState {
|
switch callContextState.remoteAudioState {
|
||||||
case .active:
|
case .active:
|
||||||
@@ -453,7 +453,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
mappedVideoState = previousVideoState
|
mappedVideoState = previousVideoState
|
||||||
} else {
|
} else {
|
||||||
if self.isVideo {
|
if self.isVideo {
|
||||||
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
mappedVideoState = .active(isScreencast: self.isScreencastActive, endpointId: "")
|
||||||
} else if self.isVideoPossible && sessionState.isVideoPossible {
|
} else if self.isVideoPossible && sessionState.isVideoPossible {
|
||||||
mappedVideoState = .inactive
|
mappedVideoState = .inactive
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -77,18 +77,18 @@ private final class FetchImpl {
|
|||||||
let partRange: Range<Int64>
|
let partRange: Range<Int64>
|
||||||
let fetchRange: Range<Int64>
|
let fetchRange: Range<Int64>
|
||||||
let fetchedData: Data
|
let fetchedData: Data
|
||||||
let decryptedData: Data
|
let cleanData: Data
|
||||||
|
|
||||||
init(
|
init(
|
||||||
partRange: Range<Int64>,
|
partRange: Range<Int64>,
|
||||||
fetchRange: Range<Int64>,
|
fetchRange: Range<Int64>,
|
||||||
fetchedData: Data,
|
fetchedData: Data,
|
||||||
decryptedData: Data
|
cleanData: Data
|
||||||
) {
|
) {
|
||||||
self.partRange = partRange
|
self.partRange = partRange
|
||||||
self.fetchRange = fetchRange
|
self.fetchRange = fetchRange
|
||||||
self.fetchedData = fetchedData
|
self.fetchedData = fetchedData
|
||||||
self.decryptedData = decryptedData
|
self.cleanData = cleanData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +148,48 @@ private final class FetchImpl {
|
|||||||
case cdn(CdnData)
|
case cdn(CdnData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class DecryptionState {
|
||||||
|
let aesKey: Data
|
||||||
|
var aesIv: Data
|
||||||
|
let decryptedSize: Int64
|
||||||
|
var offset: Int = 0
|
||||||
|
|
||||||
|
init(aesKey: Data, aesIv: Data, decryptedSize: Int64) {
|
||||||
|
self.aesKey = aesKey
|
||||||
|
self.aesIv = aesIv
|
||||||
|
self.decryptedSize = decryptedSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryDecrypt(data: Data, offset: Int, loggingIdentifier: String) -> Data? {
|
||||||
|
if offset == self.offset {
|
||||||
|
var decryptedData = data
|
||||||
|
if self.decryptedSize == 0 {
|
||||||
|
Logger.shared.log("FetchV2", "\(loggingIdentifier): not decrypting part \(offset) ..< \(offset + data.count) (decryptedSize == 0)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if decryptedData.count % 16 != 0 {
|
||||||
|
Logger.shared.log("FetchV2", "\(loggingIdentifier): not decrypting part \(offset) ..< \(offset + data.count) (decryptedData.count % 16 != 0)")
|
||||||
|
}
|
||||||
|
let decryptedDataCount = decryptedData.count
|
||||||
|
decryptedData.withUnsafeMutableBytes { rawBytes -> Void in
|
||||||
|
let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
self.aesIv.withUnsafeMutableBytes { rawIv -> Void in
|
||||||
|
let iv = rawIv.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
MTAesDecryptBytesInplaceAndModifyIv(bytes, decryptedDataCount, self.aesKey, iv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.offset + decryptedData.count > self.decryptedSize {
|
||||||
|
decryptedData.count = Int(self.decryptedSize) - self.offset
|
||||||
|
}
|
||||||
|
self.offset += decryptedData.count
|
||||||
|
Logger.shared.log("FetchV2", "\(loggingIdentifier): decrypted part \(offset) ..< \(offset + data.count) (new offset is \(self.offset))")
|
||||||
|
return decryptedData
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class FetchingState {
|
private final class FetchingState {
|
||||||
let fetchLocation: FetchLocation
|
let fetchLocation: FetchLocation
|
||||||
let partSize: Int64
|
let partSize: Int64
|
||||||
@@ -160,6 +202,7 @@ private final class FetchImpl {
|
|||||||
var pendingParts: [PendingPart] = []
|
var pendingParts: [PendingPart] = []
|
||||||
var completedRanges = RangeSet<Int64>()
|
var completedRanges = RangeSet<Int64>()
|
||||||
|
|
||||||
|
var decryptionState: DecryptionState?
|
||||||
var pendingReadyParts: [PendingReadyPart] = []
|
var pendingReadyParts: [PendingReadyPart] = []
|
||||||
var completedHashRanges = RangeSet<Int64>()
|
var completedHashRanges = RangeSet<Int64>()
|
||||||
var pendingHashRanges: [PendingHashRange] = []
|
var pendingHashRanges: [PendingHashRange] = []
|
||||||
@@ -174,7 +217,8 @@ private final class FetchImpl {
|
|||||||
maxPartSize: Int64,
|
maxPartSize: Int64,
|
||||||
partAlignment: Int64,
|
partAlignment: Int64,
|
||||||
partDivision: Int64,
|
partDivision: Int64,
|
||||||
maxPendingParts: Int
|
maxPendingParts: Int,
|
||||||
|
decryptionState: DecryptionState?
|
||||||
) {
|
) {
|
||||||
self.fetchLocation = fetchLocation
|
self.fetchLocation = fetchLocation
|
||||||
self.partSize = partSize
|
self.partSize = partSize
|
||||||
@@ -183,6 +227,7 @@ private final class FetchImpl {
|
|||||||
self.partAlignment = partAlignment
|
self.partAlignment = partAlignment
|
||||||
self.partDivision = partDivision
|
self.partDivision = partDivision
|
||||||
self.maxPendingParts = maxPendingParts
|
self.maxPendingParts = maxPendingParts
|
||||||
|
self.decryptionState = decryptionState
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@@ -373,6 +418,12 @@ private final class FetchImpl {
|
|||||||
if self.state == nil {
|
if self.state == nil {
|
||||||
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): initializing to .datacenter(\(self.datacenterId))")
|
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): initializing to .datacenter(\(self.datacenterId))")
|
||||||
|
|
||||||
|
var decryptionState: DecryptionState?
|
||||||
|
if let encryptionKey = self.encryptionKey, let decryptedSize = self.decryptedSize {
|
||||||
|
decryptionState = DecryptionState(aesKey: encryptionKey.aesKey, aesIv: encryptionKey.aesIv, decryptedSize: decryptedSize)
|
||||||
|
self.onNext(.reset)
|
||||||
|
}
|
||||||
|
|
||||||
self.state = .fetching(FetchingState(
|
self.state = .fetching(FetchingState(
|
||||||
fetchLocation: .datacenter(self.datacenterId),
|
fetchLocation: .datacenter(self.datacenterId),
|
||||||
partSize: self.defaultPartSize,
|
partSize: self.defaultPartSize,
|
||||||
@@ -380,7 +431,8 @@ private final class FetchImpl {
|
|||||||
maxPartSize: 1 * 1024 * 1024,
|
maxPartSize: 1 * 1024 * 1024,
|
||||||
partAlignment: 4 * 1024,
|
partAlignment: 4 * 1024,
|
||||||
partDivision: 1 * 1024 * 1024,
|
partDivision: 1 * 1024 * 1024,
|
||||||
maxPendingParts: 6
|
maxPendingParts: 6,
|
||||||
|
decryptionState: decryptionState
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
guard let state = self.state else {
|
guard let state = self.state else {
|
||||||
@@ -396,6 +448,25 @@ private final class FetchImpl {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
var removedPendingReadyPartIndices: [Int] = []
|
var removedPendingReadyPartIndices: [Int] = []
|
||||||
|
if let decryptionState = state.decryptionState {
|
||||||
|
while true {
|
||||||
|
var removedSomePendingReadyPart = false
|
||||||
|
for i in 0 ..< state.pendingReadyParts.count {
|
||||||
|
if removedPendingReadyPartIndices.contains(i) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let pendingReadyPart = state.pendingReadyParts[i]
|
||||||
|
if let resultData = decryptionState.tryDecrypt(data: pendingReadyPart.cleanData, offset: Int(pendingReadyPart.fetchRange.lowerBound), loggingIdentifier: self.loggingIdentifier) {
|
||||||
|
removedPendingReadyPartIndices.append(i)
|
||||||
|
removedSomePendingReadyPart = true
|
||||||
|
self.commitPendingReadyPart(state: state, partRange: pendingReadyPart.partRange, fetchRange: pendingReadyPart.fetchRange, data: resultData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !removedSomePendingReadyPart {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for i in 0 ..< state.pendingReadyParts.count {
|
for i in 0 ..< state.pendingReadyParts.count {
|
||||||
let pendingReadyPart = state.pendingReadyParts[i]
|
let pendingReadyPart = state.pendingReadyParts[i]
|
||||||
if state.completedHashRanges.isSuperset(of: RangeSet<Int64>(pendingReadyPart.fetchRange)) {
|
if state.completedHashRanges.isSuperset(of: RangeSet<Int64>(pendingReadyPart.fetchRange)) {
|
||||||
@@ -422,7 +493,7 @@ private final class FetchImpl {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataToHash = pendingReadyPart.decryptedData.subdata(in: Int(partLocalHashRange.lowerBound) ..< Int(partLocalHashRange.upperBound))
|
let dataToHash = pendingReadyPart.cleanData.subdata(in: Int(partLocalHashRange.lowerBound) ..< Int(partLocalHashRange.upperBound))
|
||||||
let localHash = MTSha256(dataToHash)
|
let localHash = MTSha256(dataToHash)
|
||||||
if localHash != hashRange.data {
|
if localHash != hashRange.data {
|
||||||
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): failed to verify \(pendingReadyPart.fetchRange): hash mismatch")
|
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): failed to verify \(pendingReadyPart.fetchRange): hash mismatch")
|
||||||
@@ -438,13 +509,14 @@ private final class FetchImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !checkFailed {
|
if !checkFailed {
|
||||||
self.commitPendingReadyPart(state: state, partRange: pendingReadyPart.partRange, fetchRange: pendingReadyPart.fetchRange, data: pendingReadyPart.decryptedData)
|
self.commitPendingReadyPart(state: state, partRange: pendingReadyPart.partRange, fetchRange: pendingReadyPart.fetchRange, data: pendingReadyPart.cleanData)
|
||||||
} else {
|
} else {
|
||||||
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): unable to find \(pendingReadyPart.fetchRange) hash check failed")
|
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): unable to find \(pendingReadyPart.fetchRange) hash check failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for index in removedPendingReadyPartIndices.reversed() {
|
}
|
||||||
|
for index in removedPendingReadyPartIndices.sorted(by: >) {
|
||||||
state.pendingReadyParts.remove(at: index)
|
state.pendingReadyParts.remove(at: index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,8 +524,10 @@ private final class FetchImpl {
|
|||||||
var requiredHashRanges = RangeSet<Int64>()
|
var requiredHashRanges = RangeSet<Int64>()
|
||||||
for pendingReadyPart in state.pendingReadyParts {
|
for pendingReadyPart in state.pendingReadyParts {
|
||||||
//TODO:check if already have hashes
|
//TODO:check if already have hashes
|
||||||
|
if state.decryptionState == nil {
|
||||||
requiredHashRanges.formUnion(RangeSet<Int64>(pendingReadyPart.fetchRange))
|
requiredHashRanges.formUnion(RangeSet<Int64>(pendingReadyPart.fetchRange))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
requiredHashRanges.subtract(state.completedHashRanges)
|
requiredHashRanges.subtract(state.completedHashRanges)
|
||||||
for pendingHashRange in state.pendingHashRanges {
|
for pendingHashRange in state.pendingHashRanges {
|
||||||
requiredHashRanges.subtract(RangeSet<Int64>(pendingHashRange.range))
|
requiredHashRanges.subtract(RangeSet<Int64>(pendingHashRange.range))
|
||||||
@@ -613,7 +687,8 @@ private final class FetchImpl {
|
|||||||
maxPartSize: self.cdnPartSize * 2,
|
maxPartSize: self.cdnPartSize * 2,
|
||||||
partAlignment: self.cdnPartSize,
|
partAlignment: self.cdnPartSize,
|
||||||
partDivision: 1 * 1024 * 1024,
|
partDivision: 1 * 1024 * 1024,
|
||||||
maxPendingParts: 6
|
maxPendingParts: 6,
|
||||||
|
decryptionState: nil
|
||||||
))
|
))
|
||||||
self.update()
|
self.update()
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
@@ -661,7 +736,8 @@ private final class FetchImpl {
|
|||||||
maxPartSize: self.defaultPartSize,
|
maxPartSize: self.defaultPartSize,
|
||||||
partAlignment: 4 * 1024,
|
partAlignment: 4 * 1024,
|
||||||
partDivision: 1 * 1024 * 1024,
|
partDivision: 1 * 1024 * 1024,
|
||||||
maxPendingParts: 6
|
maxPendingParts: 6,
|
||||||
|
decryptionState: nil
|
||||||
))
|
))
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
@@ -819,7 +895,16 @@ private final class FetchImpl {
|
|||||||
partRange: partRange,
|
partRange: partRange,
|
||||||
fetchRange: fetchRange,
|
fetchRange: fetchRange,
|
||||||
fetchedData: verifyPartHashData.fetchedData,
|
fetchedData: verifyPartHashData.fetchedData,
|
||||||
decryptedData: data
|
cleanData: data
|
||||||
|
))
|
||||||
|
} else if state.decryptionState != nil {
|
||||||
|
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): stashing data part \(partRange) (aligned as \(fetchRange)) for decryption")
|
||||||
|
|
||||||
|
state.pendingReadyParts.append(FetchImpl.PendingReadyPart(
|
||||||
|
partRange: partRange,
|
||||||
|
fetchRange: fetchRange,
|
||||||
|
fetchedData: data,
|
||||||
|
cleanData: data
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
self.commitPendingReadyPart(
|
self.commitPendingReadyPart(
|
||||||
@@ -837,7 +922,8 @@ private final class FetchImpl {
|
|||||||
maxPartSize: self.cdnPartSize * 2,
|
maxPartSize: self.cdnPartSize * 2,
|
||||||
partAlignment: self.cdnPartSize,
|
partAlignment: self.cdnPartSize,
|
||||||
partDivision: 1 * 1024 * 1024,
|
partDivision: 1 * 1024 * 1024,
|
||||||
maxPendingParts: 6
|
maxPendingParts: 6,
|
||||||
|
decryptionState: nil
|
||||||
))
|
))
|
||||||
case let .cdnRefresh(cdnData, refreshToken):
|
case let .cdnRefresh(cdnData, refreshToken):
|
||||||
self.state = .reuploadingToCdn(ReuploadingToCdnState(
|
self.state = .reuploadingToCdn(ReuploadingToCdnState(
|
||||||
|
|||||||
@@ -1115,7 +1115,7 @@ func multipartFetch(
|
|||||||
continueInBackground: Bool = false,
|
continueInBackground: Bool = false,
|
||||||
useMainConnection: Bool = false
|
useMainConnection: Bool = false
|
||||||
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||||
if network.useExperimentalFeatures, let _ = resource as? TelegramCloudMediaResource, !(resource is SecretFileMediaResource) {
|
if network.useExperimentalFeatures, let _ = resource as? TelegramCloudMediaResource {
|
||||||
return multipartFetchV2(
|
return multipartFetchV2(
|
||||||
accountPeerId: accountPeerId,
|
accountPeerId: accountPeerId,
|
||||||
postbox: postbox,
|
postbox: postbox,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ swift_library(
|
|||||||
"//submodules/AppBundle",
|
"//submodules/AppBundle",
|
||||||
"//submodules/UIKitRuntimeUtils",
|
"//submodules/UIKitRuntimeUtils",
|
||||||
"//submodules/TelegramPresentationData",
|
"//submodules/TelegramPresentationData",
|
||||||
|
"//submodules/Components/MultilineTextComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@@ -317,6 +317,10 @@ public class ShareRootControllerImpl {
|
|||||||
presentationDataPromise.set(.single(presentationData))
|
presentationDataPromise.set(.single(presentationData))
|
||||||
|
|
||||||
var immediatePeerId: PeerId?
|
var immediatePeerId: PeerId?
|
||||||
|
#if DEBUG
|
||||||
|
// Xcode crashes
|
||||||
|
immediatePeerId = nil
|
||||||
|
#else
|
||||||
if #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent {
|
if #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent {
|
||||||
if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
||||||
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
|
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
|
||||||
@@ -325,6 +329,7 @@ public class ShareRootControllerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*let account: Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> = internalContext.sharedContext.accountManager.transaction { transaction -> (SharedAccountContextImpl, LoggingSettings) in
|
/*let account: Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> = internalContext.sharedContext.accountManager.transaction { transaction -> (SharedAccountContextImpl, LoggingSettings) in
|
||||||
return (internalContext.sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings)
|
return (internalContext.sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings)
|
||||||
|
|||||||
@@ -885,6 +885,39 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.groupCallDisposable = (callManager.currentGroupCallSignal
|
self.groupCallDisposable = (callManager.currentGroupCallSignal
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
if strongSelf.immediateExperimentalUISettings.conferenceCalls {
|
||||||
|
let mappedCall = call.flatMap(CallController.Call.groupCall)
|
||||||
|
if mappedCall != strongSelf.callController?.call {
|
||||||
|
strongSelf.callController?.dismiss()
|
||||||
|
strongSelf.callController = nil
|
||||||
|
strongSelf.hasOngoingCall.set(false)
|
||||||
|
|
||||||
|
if let call {
|
||||||
|
mainWindow.hostView.containerView.endEditing(true)
|
||||||
|
|
||||||
|
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||||
|
|
||||||
|
let callController = CallController(sharedContext: strongSelf, account: call.accountContext.account, call: .groupCall(call), easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||||
|
|
||||||
|
callController.onViewDidAppear = { [weak strongSelf] in
|
||||||
|
if let strongSelf {
|
||||||
|
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callController.onViewDidDisappear = { [weak strongSelf] in
|
||||||
|
if let strongSelf {
|
||||||
|
strongSelf.hasGroupCallOnScreenPromise.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.callController = callController
|
||||||
|
strongSelf.mainWindow?.present(callController, on: .calls)
|
||||||
|
|
||||||
|
strongSelf.hasOngoingCall.set(true)
|
||||||
|
} else {
|
||||||
|
strongSelf.hasOngoingCall.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if call !== strongSelf.groupCallController?.call {
|
if call !== strongSelf.groupCallController?.call {
|
||||||
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
||||||
strongSelf.groupCallController = nil
|
strongSelf.groupCallController = nil
|
||||||
@@ -943,6 +976,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let callSignal: Signal<PresentationCall?, NoError> = .single(nil)
|
let callSignal: Signal<PresentationCall?, NoError> = .single(nil)
|
||||||
@@ -1178,7 +1212,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let currentCallController = self.callController {
|
if let currentCallController = self.callController {
|
||||||
if currentCallController.call === call {
|
if currentCallController.call == .call(call) {
|
||||||
self.navigateToCurrentCall()
|
self.navigateToCurrentCall()
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@@ -1188,7 +1222,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.mainWindow?.hostView.containerView.endEditing(true)
|
self.mainWindow?.hostView.containerView.endEditing(true)
|
||||||
let callController = CallController(sharedContext: self, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
let callController = CallController(sharedContext: self, account: call.context.account, call: .call(call), easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||||
self.callController = callController
|
self.callController = callController
|
||||||
callController.restoreUIForPictureInPicture = { [weak self, weak callController] completion in
|
callController.restoreUIForPictureInPicture = { [weak self, weak callController] completion in
|
||||||
guard let self, let callController else {
|
guard let self, let callController else {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
public var dynamicStreaming: Bool
|
public var dynamicStreaming: Bool
|
||||||
public var enableLocalTranslation: Bool
|
public var enableLocalTranslation: Bool
|
||||||
public var autoBenchmarkReflectors: Bool?
|
public var autoBenchmarkReflectors: Bool?
|
||||||
|
public var conferenceCalls: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@@ -101,7 +102,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
liveStreamV2: false,
|
liveStreamV2: false,
|
||||||
dynamicStreaming: false,
|
dynamicStreaming: false,
|
||||||
enableLocalTranslation: false,
|
enableLocalTranslation: false,
|
||||||
autoBenchmarkReflectors: nil
|
autoBenchmarkReflectors: nil,
|
||||||
|
conferenceCalls: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +144,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
liveStreamV2: Bool,
|
liveStreamV2: Bool,
|
||||||
dynamicStreaming: Bool,
|
dynamicStreaming: Bool,
|
||||||
enableLocalTranslation: Bool,
|
enableLocalTranslation: Bool,
|
||||||
autoBenchmarkReflectors: Bool?
|
autoBenchmarkReflectors: Bool?,
|
||||||
|
conferenceCalls: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@@ -181,6 +184,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.dynamicStreaming = dynamicStreaming
|
self.dynamicStreaming = dynamicStreaming
|
||||||
self.enableLocalTranslation = enableLocalTranslation
|
self.enableLocalTranslation = enableLocalTranslation
|
||||||
self.autoBenchmarkReflectors = autoBenchmarkReflectors
|
self.autoBenchmarkReflectors = autoBenchmarkReflectors
|
||||||
|
self.conferenceCalls = conferenceCalls
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@@ -223,6 +227,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.dynamicStreaming = try container.decodeIfPresent(Bool.self, forKey: "dynamicStreaming_v2") ?? false
|
self.dynamicStreaming = try container.decodeIfPresent(Bool.self, forKey: "dynamicStreaming_v2") ?? false
|
||||||
self.enableLocalTranslation = try container.decodeIfPresent(Bool.self, forKey: "enableLocalTranslation") ?? false
|
self.enableLocalTranslation = try container.decodeIfPresent(Bool.self, forKey: "enableLocalTranslation") ?? false
|
||||||
self.autoBenchmarkReflectors = try container.decodeIfPresent(Bool.self, forKey: "autoBenchmarkReflectors")
|
self.autoBenchmarkReflectors = try container.decodeIfPresent(Bool.self, forKey: "autoBenchmarkReflectors")
|
||||||
|
self.conferenceCalls = try container.decodeIfPresent(Bool.self, forKey: "conferenceCalls") ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@@ -265,6 +270,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
try container.encode(self.dynamicStreaming, forKey: "dynamicStreaming")
|
try container.encode(self.dynamicStreaming, forKey: "dynamicStreaming")
|
||||||
try container.encode(self.enableLocalTranslation, forKey: "enableLocalTranslation")
|
try container.encode(self.enableLocalTranslation, forKey: "enableLocalTranslation")
|
||||||
try container.encodeIfPresent(self.autoBenchmarkReflectors, forKey: "autoBenchmarkReflectors")
|
try container.encodeIfPresent(self.autoBenchmarkReflectors, forKey: "autoBenchmarkReflectors")
|
||||||
|
try container.encodeIfPresent(self.conferenceCalls, forKey: "conferenceCalls")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user