Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-07-24 09:52:53 +03:00
commit e9eac3eac0
32 changed files with 437 additions and 294 deletions

View File

@ -145,6 +145,7 @@ objc_library(
], ],
deps = [ deps = [
"//submodules/NumberPluralizationForm:NumberPluralizationForm", "//submodules/NumberPluralizationForm:NumberPluralizationForm",
"//submodules/AppBundle:AppBundle",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -70,15 +70,22 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination") let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")
self.screencastBufferClientContext = screencastBufferClientContext self.screencastBufferClientContext = screencastBufferClientContext
var wasRunning = false
self.statusDisposable = (screencastBufferClientContext.status self.statusDisposable = (screencastBufferClientContext.status
|> deliverOnMainQueue).start(next: { [weak self] status in |> deliverOnMainQueue).start(next: { [weak self] status in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
switch status { switch status {
case .active:
wasRunning = true
case let .finished(reason): case let .finished(reason):
if wasRunning {
strongSelf.finish(with: .screencastEnded)
} else {
strongSelf.finish(with: reason) strongSelf.finish(with: reason)
} }
}
}) })
} }

View File

@ -5509,6 +5509,7 @@ Sorry for the inconvenience.";
"Stats.MessagePublicForwardsTitle" = "Public Shares"; "Stats.MessagePublicForwardsTitle" = "Public Shares";
"Call.CameraTooltip" = "Tap here to turn on your camera"; "Call.CameraTooltip" = "Tap here to turn on your camera";
"Call.CameraOrScreenTooltip" = "Turn on your camera or screensharing";
"Call.CameraConfirmationText" = "Switch to video call?"; "Call.CameraConfirmationText" = "Switch to video call?";
"Call.CameraConfirmationConfirm" = "Switch"; "Call.CameraConfirmationConfirm" = "Switch";

View File

@ -308,6 +308,7 @@ static _FormattedString * _Nonnull getFormatted{num_arguments}(_PresentationStri
#import <PresentationStrings/PresentationStrings.h> #import <PresentationStrings/PresentationStrings.h>
#import <NumberPluralizationForm/NumberPluralizationForm.h> #import <NumberPluralizationForm/NumberPluralizationForm.h>
#import <AppBundle/AppBundle.h>
@implementation _FormattedStringRange @implementation _FormattedStringRange
@ -447,7 +448,7 @@ static NSString * _Nonnull getSingle(_PresentationStrings * _Nonnull strings, NS
static NSDictionary<NSString *, NSString *> *fallbackDict = nil; static NSDictionary<NSString *, NSString *> *fallbackDict = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
NSString *lprojPath = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]; NSString *lprojPath = [getAppBundle() pathForResource:@"en" ofType:@"lproj"];
if (!lprojPath) { if (!lprojPath) {
return; return;
} }
@ -496,7 +497,7 @@ static NSString * _Nonnull getPluralizedIndirect(_PresentationStrings * _Nonnull
static NSDictionary<NSNumber *, NSString *> *idToKey = nil; static NSDictionary<NSNumber *, NSString *> *idToKey = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"PresentationStrings" ofType:@"data"]; NSString *dataPath = [getAppBundle() pathForResource:@"PresentationStrings" ofType:@"data"];
if (!dataPath) { if (!dataPath) {
assert(false); assert(false);
return; return;

View File

@ -568,7 +568,7 @@ public protocol SharedAccountContext: class {
var callManager: PresentationCallManager? { get } var callManager: PresentationCallManager? { get }
var contactDataManager: DeviceContactDataManager? { get } var contactDataManager: DeviceContactDataManager? { get }
var activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get } var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get }
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get } var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
var presentGlobalController: (ViewController, Any?) -> Void { get } var presentGlobalController: (ViewController, Any?) -> Void { get }

View File

@ -57,8 +57,8 @@ public struct PresentationCallState: Equatable {
public enum VideoState: Equatable { public enum VideoState: Equatable {
case notAvailable case notAvailable
case inactive case inactive
case active case active(isScreencast: Bool)
case paused case paused(isScreencast: Bool)
} }
public enum RemoteVideoState: Equatable { public enum RemoteVideoState: Equatable {
@ -132,7 +132,7 @@ public final class PresentationCallVideoView {
} }
public protocol PresentationCall: class { public protocol PresentationCall: class {
var account: Account { get } var context: AccountContext { get }
var isIntegratedWithCallKit: Bool { get } var isIntegratedWithCallKit: Bool { get }
var internalId: CallSessionInternalId { get } var internalId: CallSessionInternalId { get }
var peerId: PeerId { get } var peerId: PeerId { get }

View File

@ -7,38 +7,38 @@ import AccountContext
public let maximumNumberOfAccounts = 3 public let maximumNumberOfAccounts = 3
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> { public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]), NoError> {
let sharedContext = context.sharedContext let sharedContext = context.sharedContext
return context.sharedContext.activeAccounts return context.sharedContext.activeAccountContexts
|> mapToSignal { primary, activeAccounts, _ -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> in |> mapToSignal { primary, activeAccounts, _ -> Signal<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]), NoError> in
var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = [] var accounts: [Signal<(AccountContext, Peer, Int32)?, NoError>] = []
func accountWithPeer(_ account: Account) -> Signal<(Account, Peer, Int32)?, NoError> { func accountWithPeer(_ context: AccountContext) -> Signal<(AccountContext, Peer, Int32)?, NoError> {
return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox)) return combineLatest(context.account.postbox.peerView(id: context.account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: context.account.postbox))
|> map { view, totalUnreadCount -> (Peer?, Int32) in |> map { view, totalUnreadCount -> (Peer?, Int32) in
return (view.peers[view.peerId], totalUnreadCount.0) return (view.peers[view.peerId], totalUnreadCount.0)
} }
|> distinctUntilChanged { lhs, rhs in |> distinctUntilChanged { lhs, rhs in
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1 return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
} }
|> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in |> map { peer, totalUnreadCount -> (AccountContext, Peer, Int32)? in
if let peer = peer { if let peer = peer {
return (account, peer, totalUnreadCount) return (context, peer, totalUnreadCount)
} else { } else {
return nil return nil
} }
} }
} }
for (_, account, _) in activeAccounts { for (_, context, _) in activeAccounts {
accounts.append(accountWithPeer(account)) accounts.append(accountWithPeer(context))
} }
return combineLatest(accounts) return combineLatest(accounts)
|> map { accounts -> ((Account, Peer)?, [(Account, Peer, Int32)]) in |> map { accounts -> ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]) in
var primaryRecord: (Account, Peer)? var primaryRecord: (AccountContext, Peer)?
if let first = accounts.filter({ $0?.0.id == primary?.id }).first, let (account, peer, _) = first { if let first = accounts.filter({ $0?.0.account.id == primary?.account.id }).first, let (account, peer, _) = first {
primaryRecord = (account, peer) primaryRecord = (account, peer)
} }
let accountRecords: [(Account, Peer, Int32)] = (includePrimary ? accounts : accounts.filter({ $0?.0.id != primary?.id })).compactMap({ $0 }) let accountRecords: [(AccountContext, Peer, Int32)] = (includePrimary ? accounts : accounts.filter({ $0?.0.account.id != primary?.account.id })).compactMap({ $0 })
return (primaryRecord, accountRecords) return (primaryRecord, accountRecords)
} }
} }

View File

@ -144,7 +144,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
return message.text return message.text
} }
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? { public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? {
let message = entry.message let message = entry.message
let location = entry.location let location = entry.location
if let (media, mediaImage) = mediaForMessage(message: message) { if let (media, mediaImage) = mediaForMessage(message: message) {
@ -348,7 +348,7 @@ public class GalleryController: ViewController, StandalonePresentableController
private let fromPlayingVideo: Bool private let fromPlayingVideo: Bool
private let landscape: Bool private let landscape: Bool
private let timecode: Double? private let timecode: Double?
private let playbackRate: Double private let playbackRate: Double?
private let accountInUseDisposable = MetaDisposable() private let accountInUseDisposable = MetaDisposable()
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
@ -388,7 +388,7 @@ public class GalleryController: ViewController, StandalonePresentableController
private var initialOrientation: UIInterfaceOrientation? private var initialOrientation: UIInterfaceOrientation?
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) { public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
self.context = context self.context = context
self.source = source self.source = source
self.invertItemOrder = invertItemOrder self.invertItemOrder = invertItemOrder
@ -538,7 +538,7 @@ public class GalleryController: ViewController, StandalonePresentableController
if entry.message.stableId == strongSelf.centralEntryStableId { if entry.message.stableId == strongSelf.centralEntryStableId {
isCentral = true isCentral = true
} }
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: isCentral ? playbackRate : 1.0, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: isCentral ? playbackRate : nil, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in
if let strongSelf = self { if let strongSelf = self {
strongSelf.presentInGlobalOverlay(c, with: a) strongSelf.presentInGlobalOverlay(c, with: a)
} }

View File

@ -44,7 +44,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
let isSecret: Bool let isSecret: Bool
let landscape: Bool let landscape: Bool
let timecode: Double? let timecode: Double?
let playbackRate: Double let playbackRate: Double?
let configuration: GalleryConfiguration? let configuration: GalleryConfiguration?
let playbackCompleted: () -> Void let playbackCompleted: () -> Void
let performAction: (GalleryControllerInteractionTapAction) -> Void let performAction: (GalleryControllerInteractionTapAction) -> Void
@ -52,7 +52,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void
let present: (ViewController, Any?) -> Void let present: (ViewController, Any?) -> Void
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) { public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) {
self.context = context self.context = context
self.presentationData = presentationData self.presentationData = presentationData
self.content = content self.content = content
@ -1314,7 +1314,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
var isAnimated = false var isAnimated = false
var seek = MediaPlayerSeek.start var seek = MediaPlayerSeek.start
var playbackRate: Double = 1.0 var playbackRate: Double? = nil
if let item = self.item { if let item = self.item {
if let content = item.content as? NativeVideoContent { if let content = item.content as? NativeVideoContent {
isAnimated = content.fileReference.media.isAnimated isAnimated = content.fileReference.media.isAnimated
@ -1329,7 +1329,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
playbackRate = item.playbackRate playbackRate = item.playbackRate
} }
} }
if let playbackRate = playbackRate {
videoNode.setBaseRate(playbackRate) videoNode.setBaseRate(playbackRate)
}
if isAnimated { if isAnimated {
videoNode.seek(0.0) videoNode.seek(0.0)
videoNode.play() videoNode.play()
@ -2056,6 +2058,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
f(.default) f(.default)
if let strongSelf = self { if let strongSelf = self {
switch strongSelf.fetchStatus {
case .Local:
let _ = (SaveToCameraRoll.saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .message(message: MessageReference(message), media: file)) let _ = (SaveToCameraRoll.saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .message(message: MessageReference(message), media: file))
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
guard let strongSelf = self else { guard let strongSelf = self else {
@ -2067,6 +2071,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
//TODO:localize //TODO:localize
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: "Video Saved"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: "Video Saved"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}) })
default:
guard let controller = strongSelf.galleryController() else {
return
}
//TODO:localize
controller.present(textAlertController(context: strongSelf.context, title: nil, text: "Please wait for the video to be fully downloaded.", actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
} }
}))) })))
} }

View File

@ -312,7 +312,7 @@ public func intentsSettingsController(context: AccountContext) -> ViewController
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.intentsSettings] as? IntentsSettings) ?? IntentsSettings.defaultSettings let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.intentsSettings] as? IntentsSettings) ?? IntentsSettings.defaultSettings
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.IntentsSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.IntentsSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: intentsSettingsControllerEntries(context: context, presentationData: presentationData, settings: settings, accounts: accounts.1.map { ($0.0, $0.1) }), style: .blocks, animateChanges: false) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: intentsSettingsControllerEntries(context: context, presentationData: presentationData, settings: settings, accounts: accounts.1.map { ($0.0.account, $0.1) }), style: .blocks, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -1067,9 +1067,9 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
})) }))
} }
let hasMoreThanOneAccount = context.sharedContext.activeAccounts let hasMoreThanOneAccount = context.sharedContext.activeAccountContexts
|> map { _, accounts, _ -> Bool in |> map { _, contexts, _ -> Bool in
return accounts.count > 1 return contexts.count > 1
} }
|> distinctUntilChanged |> distinctUntilChanged

View File

@ -46,6 +46,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
case end case end
case cancel case cancel
case share case share
case screencast
} }
var appearance: Appearance var appearance: Appearance
@ -299,6 +300,10 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}) })
case .share: case .share:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallShareButton"), color: imageColor) image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallShareButton"), color: imageColor)
case .screencast:
if let iconImage = generateTintedImage(image: UIImage(bundleImageName: "Call/ScreenSharePhone"), color: imageColor) {
image = generateScaledImage(image: iconImage, size: iconImage.size.aspectFitted(CGSize(width: 38.0, height: 38.0)))
}
} }
if let image = image { if let image = image {

View File

@ -25,6 +25,7 @@ enum CallControllerButtonsMode: Equatable {
struct VideoState: Equatable { struct VideoState: Equatable {
var isAvailable: Bool var isAvailable: Bool
var isCameraActive: Bool var isCameraActive: Bool
var isScreencastActive: Bool
var canChangeStatus: Bool var canChangeStatus: Bool
var hasVideo: Bool var hasVideo: Bool
var isInitializingCamera: Bool var isInitializingCamera: Bool
@ -64,7 +65,7 @@ private enum ButtonDescription: Equatable {
case accept case accept
case end(EndType) case end(EndType)
case enableCamera(Bool, Bool, Bool) case enableCamera(isActive: Bool, isEnabled: Bool, isLoading: Bool, isScreencast: Bool)
case switchCamera(Bool) case switchCamera(Bool)
case soundOutput(SoundOutput) case soundOutput(SoundOutput)
case mute(Bool) case mute(Bool)
@ -224,15 +225,18 @@ final class CallControllerButtonsNode: ASDisplayNode {
if videoState.isAvailable { if videoState.isAvailable {
let isCameraActive: Bool let isCameraActive: Bool
let isScreencastActive: Bool
let isCameraInitializing: Bool let isCameraInitializing: Bool
if videoState.hasVideo { if videoState.hasVideo {
isCameraActive = videoState.isCameraActive isCameraActive = videoState.isCameraActive
isScreencastActive = videoState.isScreencastActive
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} else { } else {
isCameraActive = false isCameraActive = false
isScreencastActive = false
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} }
topButtons.append(.enableCamera(isCameraActive, false, isCameraInitializing)) topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: false, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
if !videoState.hasVideo { if !videoState.hasVideo {
topButtons.append(.mute(self.isMuted)) topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput)) topButtons.append(.soundOutput(soundOutput))
@ -242,8 +246,10 @@ final class CallControllerButtonsNode: ASDisplayNode {
} else { } else {
topButtons.append(.mute(self.isMuted)) topButtons.append(.mute(self.isMuted))
} }
if !isScreencastActive {
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing)) topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
} }
}
} else { } else {
topButtons.append(.mute(self.isMuted)) topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput)) topButtons.append(.soundOutput(soundOutput))
@ -280,14 +286,17 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .active: case .active:
if videoState.hasVideo { if videoState.hasVideo {
let isCameraActive: Bool let isCameraActive: Bool
let isScreencastActive: Bool
let isCameraEnabled: Bool let isCameraEnabled: Bool
let isCameraInitializing: Bool let isCameraInitializing: Bool
if videoState.hasVideo { if videoState.hasVideo {
isCameraActive = videoState.isCameraActive isCameraActive = videoState.isCameraActive
isScreencastActive = videoState.isScreencastActive
isCameraEnabled = videoState.canChangeStatus isCameraEnabled = videoState.canChangeStatus
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} else { } else {
isCameraActive = false isCameraActive = false
isScreencastActive = false
isCameraEnabled = videoState.canChangeStatus isCameraEnabled = videoState.canChangeStatus
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} }
@ -315,13 +324,15 @@ final class CallControllerButtonsNode: ASDisplayNode {
} }
} }
topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing)) topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: isCameraEnabled, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
if hasAudioRouteMenu { if hasAudioRouteMenu {
topButtons.append(.soundOutput(soundOutput)) topButtons.append(.soundOutput(soundOutput))
} else { } else {
topButtons.append(.mute(isMuted)) topButtons.append(.mute(isMuted))
} }
if !isScreencastActive {
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing)) topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
}
topButtons.append(.end(.end)) topButtons.append(.end(.end))
let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
@ -340,14 +351,17 @@ final class CallControllerButtonsNode: ASDisplayNode {
var bottomButtons: [ButtonDescription] = [] var bottomButtons: [ButtonDescription] = []
let isCameraActive: Bool let isCameraActive: Bool
let isScreencastActive: Bool
let isCameraEnabled: Bool let isCameraEnabled: Bool
let isCameraInitializing: Bool let isCameraInitializing: Bool
if videoState.hasVideo { if videoState.hasVideo {
isCameraActive = videoState.isCameraActive isCameraActive = videoState.isCameraActive
isScreencastActive = videoState.isScreencastActive
isCameraEnabled = videoState.canChangeStatus isCameraEnabled = videoState.canChangeStatus
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} else { } else {
isCameraActive = false isCameraActive = false
isScreencastActive = false
isCameraEnabled = videoState.canChangeStatus isCameraEnabled = videoState.canChangeStatus
isCameraInitializing = videoState.isInitializingCamera isCameraInitializing = videoState.isInitializingCamera
} }
@ -373,7 +387,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
} }
} }
topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing)) topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: isCameraEnabled, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
topButtons.append(.mute(self.isMuted)) topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput)) topButtons.append(.soundOutput(soundOutput))
@ -442,10 +456,10 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .end: case .end:
buttonText = strings.Call_End buttonText = strings.Call_End
} }
case let .enableCamera(isActivated, isEnabled, isInitializing): case let .enableCamera(isActivated, isEnabled, isInitializing, isScreencastActive):
buttonContent = CallControllerButtonItemNode.Content( buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: isActivated), appearance: .blurred(isFilled: isActivated),
image: .camera, image: isScreencastActive ? .screencast : .camera,
isEnabled: isEnabled, isEnabled: isEnabled,
hasProgress: isInitializing hasProgress: isInitializing
) )

View File

@ -569,7 +569,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} }
switch callState.state { switch callState.state {
case .active: case .active:
if strongSelf.outgoingVideoNodeValue == nil { var isScreencastActive = false
switch callState.videoState {
case .active(true), .paused(true):
isScreencastActive = true
default:
break
}
if isScreencastActive {
(strongSelf.call as! PresentationCallImpl).disableScreencast()
} else if strongSelf.outgoingVideoNodeValue == nil {
DeviceAccess.authorizeAccess(to: .camera(.videoCall), onlyCheck: true, presentationData: strongSelf.presentationData, present: { [weak self] c, a in DeviceAccess.authorizeAccess(to: .camera(.videoCall), onlyCheck: true, presentationData: strongSelf.presentationData, present: { [weak self] c, a in
if let strongSelf = self { if let strongSelf = self {
strongSelf.present?(c) strongSelf.present?(c)
@ -620,10 +630,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
updateLayoutImpl?(layout, navigationBarHeight) updateLayoutImpl?(layout, navigationBarHeight)
}) })
let controller = VoiceChatCameraPreviewController(sharedContext: strongSelf.sharedContext, cameraNode: outgoingVideoNode, shareCamera: { [weak self] _, _ in let controller = VoiceChatCameraPreviewController(sharedContext: strongSelf.sharedContext, cameraNode: outgoingVideoNode, shareCamera: { _, _ in
if let strongSelf = self {
proceed() proceed()
}
}, switchCamera: { [weak self] in }, switchCamera: { [weak self] in
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
self?.call.switchVideoCamera() self?.call.switchVideoCamera()
@ -636,14 +644,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} }
} }
}) })
// if strongSelf.displayedCameraConfirmation {
// proceed()
// } else {
// strongSelf.present?(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.Call_CameraConfirmationText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Call_CameraConfirmationConfirm, action: {
// proceed()
// })]))
// }
}) })
} else { } else {
strongSelf.call.disableVideo() strongSelf.call.disableVideo()
@ -716,7 +716,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
return return
} }
self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraOrScreenTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
})) }))
} }
@ -877,7 +877,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} }
switch callState.videoState { switch callState.videoState {
case .active, .paused: case .active(false), .paused(false):
if !self.outgoingVideoViewRequested { if !self.outgoingVideoViewRequested {
self.outgoingVideoViewRequested = true self.outgoingVideoViewRequested = true
let delayUntilInitialized = self.isRequestingVideo let delayUntilInitialized = self.isRequestingVideo
@ -959,7 +959,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} }
}) })
} }
case .notAvailable, .inactive: default:
self.candidateOutgoingVideoNodeValue = nil self.candidateOutgoingVideoNodeValue = nil
if let outgoingVideoNodeValue = self.outgoingVideoNodeValue { if let outgoingVideoNodeValue = self.outgoingVideoNodeValue {
if self.minimizedVideoNode == outgoingVideoNodeValue { if self.minimizedVideoNode == outgoingVideoNodeValue {
@ -1256,16 +1256,20 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
mode = .none mode = .none
} }
} }
var mappedVideoState = CallControllerButtonsMode.VideoState(isAvailable: false, isCameraActive: self.outgoingVideoNodeValue != nil, canChangeStatus: false, hasVideo: self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil, isInitializingCamera: self.isRequestingVideo) var mappedVideoState = CallControllerButtonsMode.VideoState(isAvailable: false, isCameraActive: self.outgoingVideoNodeValue != nil, isScreencastActive: false, canChangeStatus: false, hasVideo: self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil, isInitializingCamera: self.isRequestingVideo)
switch callState.videoState { switch callState.videoState {
case .notAvailable: case .notAvailable:
break break
case .inactive: case .inactive:
mappedVideoState.isAvailable = true mappedVideoState.isAvailable = true
mappedVideoState.canChangeStatus = true mappedVideoState.canChangeStatus = true
case .active, .paused: case .active(let isScreencast), .paused(let isScreencast):
mappedVideoState.isAvailable = true mappedVideoState.isAvailable = true
mappedVideoState.canChangeStatus = true mappedVideoState.canChangeStatus = true
if isScreencast {
mappedVideoState.isScreencastActive = true
mappedVideoState.hasVideo = true
}
} }
switch callState.state { switch callState.state {

View File

@ -7,6 +7,7 @@ import Postbox
import TelegramCore import TelegramCore
import SwiftSignalKit import SwiftSignalKit
import AppBundle import AppBundle
import AccountContext
private var sharedProviderDelegate: AnyObject? private var sharedProviderDelegate: AnyObject?
@ -28,7 +29,7 @@ public final class CallKitIntegration {
return self.audioSessionActivePromise.get() return self.audioSessionActivePromise.get()
} }
init?(startCall: @escaping (Account, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { init?(startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
if !CallKitIntegration.isAvailable { if !CallKitIntegration.isAvailable {
return nil return nil
} }
@ -48,9 +49,9 @@ public final class CallKitIntegration {
#endif #endif
} }
func startCall(account: Account, peerId: PeerId, isVideo: Bool, displayTitle: String) { func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
(sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(account: account, peerId: peerId, isVideo: isVideo, displayTitle: displayTitle) (sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: displayTitle)
self.donateIntent(peerId: peerId, displayTitle: displayTitle) self.donateIntent(peerId: peerId, displayTitle: displayTitle)
} }
} }
@ -99,9 +100,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
private let provider: CXProvider private let provider: CXProvider
private let callController = CXCallController() private let callController = CXCallController()
private var currentStartCallAccount: (UUID, Account)? private var currentStartCallAccount: (UUID, AccountContext)?
private var startCall: ((Account, UUID, String, Bool) -> Signal<Bool, NoError>)? private var startCall: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
private var answerCall: ((UUID) -> Void)? private var answerCall: ((UUID) -> Void)?
private var endCall: ((UUID) -> Signal<Bool, NoError>)? private var endCall: ((UUID) -> Signal<Bool, NoError>)?
private var setCallMuted: ((UUID, Bool) -> Void)? private var setCallMuted: ((UUID, Bool) -> Void)?
@ -119,7 +120,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
self.provider.setDelegate(self, queue: nil) self.provider.setDelegate(self, queue: nil)
} }
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (Account, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
self.audioSessionActivePromise = audioSessionActivePromise self.audioSessionActivePromise = audioSessionActivePromise
self.startCall = startCall self.startCall = startCall
self.answerCall = answerCall self.answerCall = answerCall
@ -165,9 +166,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
} }
func startCall(account: Account, peerId: PeerId, isVideo: Bool, displayTitle: String) { func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
let uuid = UUID() let uuid = UUID()
self.currentStartCallAccount = (uuid, account) self.currentStartCallAccount = (uuid, context)
let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt32Value())") let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt32Value())")
let startCallAction = CXStartCallAction(call: uuid, handle: handle) let startCallAction = CXStartCallAction(call: uuid, handle: handle)
startCallAction.contactIdentifier = displayTitle startCallAction.contactIdentifier = displayTitle
@ -215,14 +216,14 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
} }
func provider(_ provider: CXProvider, perform action: CXStartCallAction) { func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
guard let startCall = self.startCall, let (uuid, account) = self.currentStartCallAccount, uuid == action.callUUID else { guard let startCall = self.startCall, let (uuid, context) = self.currentStartCallAccount, uuid == action.callUUID else {
action.fail() action.fail()
return return
} }
self.currentStartCallAccount = nil self.currentStartCallAccount = nil
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.disposableSet.add(disposable) self.disposableSet.add(disposable)
disposable.set((startCall(account, action.callUUID, action.handle.value, action.isVideo) disposable.set((startCall(context, action.callUUID, action.handle.value, action.isVideo)
|> deliverOnMainQueue |> deliverOnMainQueue
|> afterDisposed { [weak self, weak disposable] in |> afterDisposed { [weak self, weak disposable] in
if let strongSelf = self, let disposable = disposable { if let strongSelf = self, let disposable = disposable {

View File

@ -186,7 +186,7 @@ final class PresentationCallToneRenderer {
} }
public final class PresentationCallImpl: PresentationCall { public final class PresentationCallImpl: PresentationCall {
public let account: Account public let context: AccountContext
private let audioSession: ManagedAudioSession private let audioSession: ManagedAudioSession
private let callSessionManager: CallSessionManager private let callSessionManager: CallSessionManager
private let callKitIntegration: CallKitIntegration? private let callKitIntegration: CallKitIntegration?
@ -287,10 +287,18 @@ public final class PresentationCallImpl: PresentationCall {
private var useFrontCamera: Bool = true private var useFrontCamera: Bool = true
private var videoCapturer: OngoingCallVideoCapturer? private var videoCapturer: OngoingCallVideoCapturer?
private var screencastBufferServerContext: IpcGroupCallBufferAppContext?
private var screencastCapturer: OngoingCallVideoCapturer?
private var isScreencastActive: Bool = false
private var proximityManagerIndex: Int? private var proximityManagerIndex: Int?
private let screencastFramesDisposable = MetaDisposable()
private let screencastAudioDataDisposable = MetaDisposable()
private let screencastStateDisposable = MetaDisposable()
init( init(
account: Account, context: AccountContext,
audioSession: ManagedAudioSession, audioSession: ManagedAudioSession,
callSessionManager: CallSessionManager, callSessionManager: CallSessionManager,
callKitIntegration: CallKitIntegration?, callKitIntegration: CallKitIntegration?,
@ -313,7 +321,7 @@ public final class PresentationCallImpl: PresentationCall {
enableTCP: Bool, enableTCP: Bool,
preferredVideoCodec: String? preferredVideoCodec: String?
) { ) {
self.account = account self.context = context
self.audioSession = audioSession self.audioSession = audioSession
self.callSessionManager = callSessionManager self.callSessionManager = callSessionManager
self.callKitIntegration = callKitIntegration self.callKitIntegration = callKitIntegration
@ -345,7 +353,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, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal)) self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active(isScreencast: self.isScreencastActive), 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))
} }
@ -458,6 +466,11 @@ public final class PresentationCallImpl: PresentationCall {
} }
}) })
let screencastCapturer = OngoingCallVideoCapturer(isCustom: true)
self.screencastCapturer = screencastCapturer
self.resetScreencastContext()
if callKitIntegration == nil { if callKitIntegration == nil {
self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in
} }
@ -473,6 +486,9 @@ public final class PresentationCallImpl: PresentationCall {
self.audioLevelDisposable?.dispose() self.audioLevelDisposable?.dispose()
self.batteryLevelDisposable?.dispose() self.batteryLevelDisposable?.dispose()
self.audioSessionDisposable?.dispose() self.audioSessionDisposable?.dispose()
self.screencastFramesDisposable.dispose()
self.screencastAudioDataDisposable.dispose()
self.screencastStateDisposable.dispose()
if let dropCallKitCallTimer = self.dropCallKitCallTimer { if let dropCallKitCallTimer = self.dropCallKitCallTimer {
dropCallKitCallTimer.invalidate() dropCallKitCallTimer.invalidate()
@ -530,11 +546,11 @@ public final class PresentationCallImpl: PresentationCall {
case .notAvailable: case .notAvailable:
mappedVideoState = .notAvailable mappedVideoState = .notAvailable
case .active: case .active:
mappedVideoState = .active mappedVideoState = .active(isScreencast: self.isScreencastActive)
case .inactive: case .inactive:
mappedVideoState = .inactive mappedVideoState = .inactive
case .paused: case .paused:
mappedVideoState = .paused mappedVideoState = .paused(isScreencast: self.isScreencastActive)
} }
switch callContextState.remoteVideoState { switch callContextState.remoteVideoState {
case .inactive: case .inactive:
@ -565,7 +581,7 @@ public final class PresentationCallImpl: PresentationCall {
mappedVideoState = previousVideoState mappedVideoState = previousVideoState
} else { } else {
if self.isVideo { if self.isVideo {
mappedVideoState = .active mappedVideoState = .active(isScreencast: self.isScreencastActive)
} else if self.isVideoPossible && sessionState.isVideoPossible { } else if self.isVideoPossible && sessionState.isVideoPossible {
mappedVideoState = .inactive mappedVideoState = .inactive
} else { } else {
@ -664,7 +680,7 @@ public final class PresentationCallImpl: PresentationCall {
if let _ = audioSessionControl, !wasActive || previousControl == nil { if let _ = audioSessionControl, !wasActive || previousControl == nil {
let logName = "\(id.id)_\(id.accessHash)" let logName = "\(id.id)_\(id.accessHash)"
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec) let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
self.ongoingContext = ongoingContext self.ongoingContext = ongoingContext
ongoingContext.setIsMuted(self.isMutedValue) ongoingContext.setIsMuted(self.isMutedValue)
if let requestedVideoAspect = self.requestedVideoAspect { if let requestedVideoAspect = self.requestedVideoAspect {
@ -928,6 +944,61 @@ public final class PresentationCallImpl: PresentationCall {
} }
} }
private func resetScreencastContext() {
let basePath = self.context.sharedContext.basePath + "/broadcast-coordination"
let screencastBufferServerContext = IpcGroupCallBufferAppContext(basePath: basePath)
self.screencastBufferServerContext = screencastBufferServerContext
self.screencastFramesDisposable.set((screencastBufferServerContext.frames
|> deliverOnMainQueue).start(next: { [weak screencastCapturer] screencastFrame in
guard let screencastCapturer = screencastCapturer else {
return
}
screencastCapturer.injectPixelBuffer(screencastFrame.0, rotation: screencastFrame.1)
}))
self.screencastAudioDataDisposable.set((screencastBufferServerContext.audioData
|> deliverOnMainQueue).start(next: { [weak self] data in
guard let strongSelf = self else {
return
}
strongSelf.ongoingContext?.addExternalAudioData(data: data)
}))
self.screencastStateDisposable.set((screencastBufferServerContext.isActive
|> distinctUntilChanged
|> deliverOnMainQueue).start(next: { [weak self] isActive in
guard let strongSelf = self else {
return
}
if isActive {
strongSelf.requestScreencast()
} else {
strongSelf.disableScreencast(reset: false)
}
}))
}
private func requestScreencast() {
self.disableVideo()
if let screencastCapturer = self.screencastCapturer {
self.isScreencastActive = true
self.ongoingContext?.requestVideo(screencastCapturer)
}
}
func disableScreencast(reset: Bool = true) {
if self.isScreencastActive {
if let _ = self.videoCapturer {
self.videoCapturer = nil
}
self.isScreencastActive = false
self.ongoingContext?.disableVideo()
if reset {
self.resetScreencastContext()
}
}
}
public func setOutgoingVideoIsPaused(_ isPaused: Bool) { public func setOutgoingVideoIsPaused(_ isPaused: Bool) {
self.videoCapturer?.setIsVideoEnabled(!isPaused) self.videoCapturer?.setIsVideoEnabled(!isPaused)
} }

View File

@ -113,7 +113,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference) return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
} }
public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) { public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[AccountContext], NoError>) {
self.getDeviceAccessData = getDeviceAccessData self.getDeviceAccessData = getDeviceAccessData
self.accountManager = accountManager self.accountManager = accountManager
self.audioSession = audioSession self.audioSession = audioSession
@ -121,15 +121,15 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
self.isMediaPlaying = isMediaPlaying self.isMediaPlaying = isMediaPlaying
self.resumeMediaPlayback = resumeMediaPlayback self.resumeMediaPlayback = resumeMediaPlayback
var startCallImpl: ((Account, UUID, String, Bool) -> Signal<Bool, NoError>)? var startCallImpl: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
var answerCallImpl: ((UUID) -> Void)? var answerCallImpl: ((UUID) -> Void)?
var endCallImpl: ((UUID) -> Signal<Bool, NoError>)? var endCallImpl: ((UUID) -> Signal<Bool, NoError>)?
var setCallMutedImpl: ((UUID, Bool) -> Void)? var setCallMutedImpl: ((UUID, Bool) -> Void)?
var audioSessionActivationChangedImpl: ((Bool) -> Void)? var audioSessionActivationChangedImpl: ((Bool) -> Void)?
self.callKitIntegration = CallKitIntegration(startCall: { account, uuid, handle, isVideo in self.callKitIntegration = CallKitIntegration(startCall: { context, uuid, handle, isVideo in
if let startCallImpl = startCallImpl { if let startCallImpl = startCallImpl {
return startCallImpl(account, uuid, handle, isVideo) return startCallImpl(context, uuid, handle, isVideo)
} else { } else {
return .single(false) return .single(false)
} }
@ -161,19 +161,19 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} }
|> runOn(Queue.mainQueue()) |> runOn(Queue.mainQueue())
let ringingStatesByAccount: Signal<[(Account, CallSessionRingingState, NetworkType)], NoError> = activeAccounts let ringingStatesByAccount: Signal<[(AccountContext, CallSessionRingingState, NetworkType)], NoError> = activeAccounts
|> mapToSignal { accounts -> Signal<[(Account, CallSessionRingingState, NetworkType)], NoError> in |> mapToSignal { accounts -> Signal<[(AccountContext, CallSessionRingingState, NetworkType)], NoError> in
return combineLatest(accounts.map { account -> Signal<(Account, [CallSessionRingingState], NetworkType), NoError> in return combineLatest(accounts.map { context -> Signal<(AccountContext, [CallSessionRingingState], NetworkType), NoError> in
return combineLatest(account.callSessionManager.ringingStates(), account.networkType) return combineLatest(context.account.callSessionManager.ringingStates(), context.account.networkType)
|> map { ringingStates, networkType -> (Account, [CallSessionRingingState], NetworkType) in |> map { ringingStates, networkType -> (AccountContext, [CallSessionRingingState], NetworkType) in
return (account, ringingStates, networkType) return (context, ringingStates, networkType)
} }
}) })
|> map { ringingStatesByAccount -> [(Account, CallSessionRingingState, NetworkType)] in |> map { ringingStatesByAccount -> [(AccountContext, CallSessionRingingState, NetworkType)] in
var result: [(Account, CallSessionRingingState, NetworkType)] = [] var result: [(AccountContext, CallSessionRingingState, NetworkType)] = []
for (account, states, networkType) in ringingStatesByAccount { for (context, states, networkType) in ringingStatesByAccount {
for state in states { for state in states {
result.append((account, state, networkType)) result.append((context, state, networkType))
} }
} }
return result return result
@ -181,20 +181,20 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} }
self.ringingStatesDisposable = (combineLatest(ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess) self.ringingStatesDisposable = (combineLatest(ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess)
|> mapToSignal { ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess -> Signal<([(Account, Peer, CallSessionRingingState, Bool, NetworkType)], Bool), NoError> in |> mapToSignal { ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess -> Signal<([(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], Bool), NoError> in
if ringingStatesByAccount.isEmpty { if ringingStatesByAccount.isEmpty {
return .single(([], enableCallKit && enabledMicrophoneAccess)) return .single(([], enableCallKit && enabledMicrophoneAccess))
} else { } else {
return combineLatest(ringingStatesByAccount.map { account, state, networkType -> Signal<(Account, Peer, CallSessionRingingState, Bool, NetworkType)?, NoError> in return combineLatest(ringingStatesByAccount.map { context, state, networkType -> Signal<(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)?, NoError> in
return account.postbox.transaction { transaction -> (Account, Peer, CallSessionRingingState, Bool, NetworkType)? in return context.account.postbox.transaction { transaction -> (AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)? in
if let peer = transaction.getPeer(state.peerId) { if let peer = transaction.getPeer(state.peerId) {
return (account, peer, state, transaction.isPeerContact(peerId: state.peerId), networkType) return (context, peer, state, transaction.isPeerContact(peerId: state.peerId), networkType)
} else { } else {
return nil return nil
} }
} }
}) })
|> map { ringingStatesByAccount -> ([(Account, Peer, CallSessionRingingState, Bool, NetworkType)], Bool) in |> map { ringingStatesByAccount -> ([(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], Bool) in
return (ringingStatesByAccount.compactMap({ $0 }), enableCallKit && enabledMicrophoneAccess) return (ringingStatesByAccount.compactMap({ $0 }), enableCallKit && enabledMicrophoneAccess)
} }
} }
@ -203,9 +203,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
self?.ringingStatesUpdated(ringingStates, enableCallKit: enableCallKit) self?.ringingStatesUpdated(ringingStates, enableCallKit: enableCallKit)
}) })
startCallImpl = { [weak self] account, uuid, handle, isVideo in startCallImpl = { [weak self] context, uuid, handle, isVideo in
if let strongSelf = self, let userId = Int32(handle) { if let strongSelf = self, let userId = Int32(handle) {
return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(userId)), isVideo: isVideo, internalId: uuid) return strongSelf.startCall(context: context, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(userId)), isVideo: isVideo, internalId: uuid)
|> take(1) |> take(1)
|> map { result -> Bool in |> map { result -> Bool in
return result return result
@ -272,10 +272,10 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
self.callSettingsDisposable?.dispose() self.callSettingsDisposable?.dispose()
} }
private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) { private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
if let firstState = ringingStates.first { if let firstState = ringingStates.first {
if self.currentCall == nil && self.currentGroupCall == nil { if self.currentCall == nil && self.currentGroupCall == nil {
self.currentCallDisposable.set((combineLatest(firstState.0.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1)) self.currentCallDisposable.set((combineLatest(firstState.0.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1))
|> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in |> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -288,9 +288,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
let call = PresentationCallImpl( let call = PresentationCallImpl(
account: firstState.0, context: firstState.0,
audioSession: strongSelf.audioSession, audioSession: strongSelf.audioSession,
callSessionManager: firstState.0.callSessionManager, callSessionManager: firstState.0.account.callSessionManager,
callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil,
serializedData: configuration.serializedData, serializedData: configuration.serializedData,
dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings),
@ -304,7 +304,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
proxyServer: strongSelf.proxyServer, proxyServer: strongSelf.proxyServer,
auxiliaryServers: [], auxiliaryServers: [],
currentNetworkType: firstState.4, currentNetworkType: firstState.4,
updatedNetworkType: firstState.0.networkType, updatedNetworkType: firstState.0.account.networkType,
startWithVideo: firstState.2.isVideo, startWithVideo: firstState.2.isVideo,
isVideoPossible: firstState.2.isVideoPossible, isVideoPossible: firstState.2.isVideoPossible,
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration), enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
@ -326,9 +326,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
})) }))
})) }))
} else { } else {
for (account, _, state, _, _) in ringingStates { for (context, _, state, _, _) in ringingStates {
if state.id != self.currentCall?.internalId { if state.id != self.currentCall?.internalId {
account.callSessionManager.drop(internalId: state.id, reason: .busy, debugLog: .single(nil)) context.account.callSessionManager.drop(internalId: state.id, reason: .busy, debugLog: .single(nil))
} }
} }
} }
@ -336,8 +336,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} }
public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult { public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
let account = context.account
var alreadyInCall: Bool = false var alreadyInCall: Bool = false
var alreadyInCallWithPeerId: PeerId? var alreadyInCallWithPeerId: PeerId?
@ -388,7 +386,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return EmptyDisposable return EmptyDisposable
} }
|> runOn(Queue.mainQueue()) |> runOn(Queue.mainQueue())
let postbox = account.postbox let postbox = context.account.postbox
strongSelf.startCallDisposable.set((accessEnabledSignal strongSelf.startCallDisposable.set((accessEnabledSignal
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in |> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
if !accessEnabled { if !accessEnabled {
@ -402,7 +400,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
guard let strongSelf = self, let peer = peer else { guard let strongSelf = self, let peer = peer else {
return return
} }
strongSelf.callKitIntegration?.startCall(account: account, peerId: peerId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle) strongSelf.callKitIntegration?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle)
})) }))
} }
if let currentCall = self.currentCall { if let currentCall = self.currentCall {
@ -426,7 +424,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo).start() let _ = strongSelf.startCall(context: context, peerId: peerId, isVideo: isVideo).start()
} }
if let currentCall = self.currentCall { if let currentCall = self.currentCall {
self.startCallDisposable.set((currentCall.hangUp() self.startCallDisposable.set((currentCall.hangUp()
@ -448,7 +446,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} }
private func startCall( private func startCall(
account: Account, context: AccountContext,
peerId: PeerId, peerId: PeerId,
isVideo: Bool, isVideo: Bool,
internalId: CallSessionInternalId = CallSessionInternalId() internalId: CallSessionInternalId = CallSessionInternalId()
@ -479,7 +477,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} }
|> runOn(Queue.mainQueue()) |> runOn(Queue.mainQueue())
let networkType = account.networkType let networkType = context.account.networkType
let accountManager = self.accountManager let accountManager = self.accountManager
return accessEnabledSignal return accessEnabledSignal
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in |> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
@ -487,7 +485,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return .single(false) return .single(false)
} }
let request = account.postbox.transaction { transaction -> (VideoCallsConfiguration, CachedUserData?) in let request = context.account.postbox.transaction { transaction -> (VideoCallsConfiguration, CachedUserData?) in
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
return (VideoCallsConfiguration(appConfiguration: appConfiguration), transaction.getPeerCachedData(peerId: peerId) as? CachedUserData) return (VideoCallsConfiguration(appConfiguration: appConfiguration), transaction.getPeerCachedData(peerId: peerId) as? CachedUserData)
} }
@ -506,16 +504,16 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
isVideoPossible = false isVideoPossible = false
} }
return account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId) return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId)
} }
let cachedUserData = account.postbox.transaction { transaction -> CachedUserData? in let cachedUserData = context.account.postbox.transaction { transaction -> CachedUserData? in
return transaction.getPeerCachedData(peerId: peerId) as? CachedUserData return transaction.getPeerCachedData(peerId: peerId) as? CachedUserData
} }
return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), account.postbox.peerView(id: peerId) |> map { peerView -> Bool in return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), context.account.postbox.peerView(id: peerId) |> map { peerView -> Bool in
return peerView.peerIsContact return peerView.peerIsContact
} |> take(1), account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), cachedUserData) } |> take(1), context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), cachedUserData)
|> deliverOnMainQueue |> deliverOnMainQueue
|> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, cachedUserData in |> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, cachedUserData in
if let strongSelf = self, accessEnabled { if let strongSelf = self, accessEnabled {
@ -546,9 +544,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings
let call = PresentationCallImpl( let call = PresentationCallImpl(
account: account, context: context,
audioSession: strongSelf.audioSession, audioSession: strongSelf.audioSession,
callSessionManager: account.callSessionManager, callSessionManager: context.account.callSessionManager,
callKitIntegration: callKitIntegrationIfEnabled( callKitIntegration: callKitIntegrationIfEnabled(
strongSelf.callKitIntegration, strongSelf.callKitIntegration,
settings: strongSelf.callSettings settings: strongSelf.callSettings
@ -565,7 +563,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
proxyServer: strongSelf.proxyServer, proxyServer: strongSelf.proxyServer,
auxiliaryServers: [], auxiliaryServers: [],
currentNetworkType: currentNetworkType, currentNetworkType: currentNetworkType,
updatedNetworkType: account.networkType, updatedNetworkType: context.account.networkType,
startWithVideo: isVideo, startWithVideo: isVideo,
isVideoPossible: isVideoPossible, isVideoPossible: isVideoPossible,
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration), enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),

View File

@ -423,11 +423,18 @@ public extension TelegramEngine {
public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> { public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId) return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId)
} }
public func direct_peerExportedInvitations(peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
return _internal_peerExportedInvitations(account: self.account, peerId: peerId, revoked: revoked, adminId: adminId, offsetLink: offsetLink)
}
public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext { public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext {
return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate) return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate)
} }
public func revokePersistentPeerExportedInvitation(peerId: PeerId) -> Signal<ExportedInvitation?, NoError> {
return _internal_revokePersistentPeerExportedInvitation(account: self.account, peerId: peerId)
}
public func peerInvitationImporters(peerId: PeerId, invite: ExportedInvitation) -> PeerInvitationImportersContext { public func peerInvitationImporters(peerId: PeerId, invite: ExportedInvitation) -> PeerInvitationImportersContext {
return PeerInvitationImportersContext(account: self.account, peerId: peerId, invite: invite) return PeerInvitationImportersContext(account: self.account, peerId: peerId, invite: invite)
} }

View File

@ -833,9 +833,9 @@ final class SharedApplicationContext {
presentationDataPromise.set(sharedContext.presentationData) presentationDataPromise.set(sharedContext.presentationData)
let rawAccounts = sharedContext.activeAccounts let rawAccounts = sharedContext.activeAccountContexts
|> map { _, accounts, _ -> [Account] in |> map { _, contexts, _ -> [Account] in
return accounts.map({ $0.1 }) return contexts.map({ $0.1.account })
} }
let storeQueue = Queue() let storeQueue = Queue()
let _ = ( let _ = (
@ -870,7 +870,7 @@ final class SharedApplicationContext {
} }
} }
let notificationManager = SharedNotificationManager(episodeId: self.episodeId, application: application, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccounts |> map { primary, accounts, _ in accounts.map({ ($0.1, $0.1.id == primary?.id) }) }, pollLiveLocationOnce: { accountId in let notificationManager = SharedNotificationManager(episodeId: self.episodeId, application: application, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccountContexts |> map { primary, accounts, _ in accounts.map({ ($0.1.account, $0.1.account.id == primary?.account.id) }) }, pollLiveLocationOnce: { accountId in
let _ = (self.context.get() let _ = (self.context.get()
|> filter { |> filter {
return $0 != nil return $0 != nil
@ -922,7 +922,7 @@ final class SharedApplicationContext {
return .single(nil) return .single(nil)
} }
} }
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
return sharedContext.accountUserInterfaceInUse(id) return sharedContext.accountUserInterfaceInUse(id)
}) })
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager) let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
@ -946,8 +946,8 @@ final class SharedApplicationContext {
self.context.set(self.sharedContextPromise.get() self.context.set(self.sharedContextPromise.get()
|> deliverOnMainQueue |> deliverOnMainQueue
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext?, NoError> in |> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext?, NoError> in
return sharedApplicationContext.sharedContext.activeAccounts return sharedApplicationContext.sharedContext.activeAccountContexts
|> map { primary, _, _ -> Account? in |> map { primary, _, _ -> AccountContext? in
return primary return primary
} }
|> distinctUntilChanged(isEqual: { lhs, rhs in |> distinctUntilChanged(isEqual: { lhs, rhs in
@ -956,7 +956,7 @@ final class SharedApplicationContext {
} }
return true return true
}) })
|> mapToSignal { account -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in |> mapToSignal { context -> Signal<(AccountContext, CallListSettings)?, NoError> in
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings
} }
@ -969,24 +969,18 @@ final class SharedApplicationContext {
} }
return result return result
} }
|> mapToSignal { callListSettings -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in |> map { callListSettings -> (AccountContext, CallListSettings)? in
if let account = account { if let context = context {
return account.postbox.transaction { transaction -> (Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)? in return (context, callListSettings ?? .defaultSettings)
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
let contentSettings = getContentSettings(transaction: transaction)
let appConfiguration = getAppConfiguration(transaction: transaction)
return (account, limitsConfiguration, callListSettings ?? CallListSettings.defaultSettings, contentSettings, appConfiguration)
}
} else { } else {
return .single(nil) return nil
} }
} }
} }
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { accountAndSettings -> AuthorizedApplicationContext? in |> map { accountAndSettings -> AuthorizedApplicationContext? in
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, contentSettings, appConfiguration in return accountAndSettings.flatMap { context, callListSettings in
let context = AccountContextImpl(sharedContext: sharedApplicationContext.sharedContext, account: account, limitsConfiguration: limitsConfiguration, contentSettings: contentSettings, appConfiguration: appConfiguration) return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context as! AccountContextImpl, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
let _ = (self.context.get() let _ = (self.context.get()
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { context in |> deliverOnMainQueue).start(next: { context in
@ -1002,8 +996,8 @@ final class SharedApplicationContext {
self.authContext.set(self.sharedContextPromise.get() self.authContext.set(self.sharedContextPromise.get()
|> deliverOnMainQueue |> deliverOnMainQueue
|> mapToSignal { sharedApplicationContext -> Signal<UnauthorizedApplicationContext?, NoError> in |> mapToSignal { sharedApplicationContext -> Signal<UnauthorizedApplicationContext?, NoError> in
return sharedApplicationContext.sharedContext.activeAccounts return sharedApplicationContext.sharedContext.activeAccountContexts
|> map { primary, accounts, auth -> (Account?, UnauthorizedAccount, [Account])? in |> map { primary, accounts, auth -> (AccountContext?, UnauthorizedAccount, [AccountContext])? in
if let auth = auth { if let auth = auth {
return (primary, auth, Array(accounts.map({ $0.1 }))) return (primary, auth, Array(accounts.map({ $0.1 })))
} else { } else {
@ -1018,10 +1012,10 @@ final class SharedApplicationContext {
}) })
|> mapToSignal { authAndAccounts -> Signal<(UnauthorizedAccount, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in |> mapToSignal { authAndAccounts -> Signal<(UnauthorizedAccount, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in
if let (primary, auth, accounts) = authAndAccounts { if let (primary, auth, accounts) = authAndAccounts {
let phoneNumbers = combineLatest(accounts.map { account -> Signal<(AccountRecordId, String, Bool)?, NoError> in let phoneNumbers = combineLatest(accounts.map { context -> Signal<(AccountRecordId, String, Bool)?, NoError> in
return account.postbox.transaction { transaction -> (AccountRecordId, String, Bool)? in return context.account.postbox.transaction { transaction -> (AccountRecordId, String, Bool)? in
if let phone = (transaction.getPeer(account.peerId) as? TelegramUser)?.phone { if let phone = (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone {
return (account.id, phone, account.testingEnvironment) return (context.account.id, phone, context.account.testingEnvironment)
} else { } else {
return nil return nil
} }
@ -1032,7 +1026,7 @@ final class SharedApplicationContext {
var primaryNumber: (String, AccountRecordId, Bool)? var primaryNumber: (String, AccountRecordId, Bool)?
if let primary = primary { if let primary = primary {
for idAndNumber in phoneNumbers { for idAndNumber in phoneNumbers {
if let (id, number, testingEnvironment) = idAndNumber, id == primary.id { if let (id, number, testingEnvironment) = idAndNumber, id == primary.account.id {
primaryNumber = (number, id, testingEnvironment) primaryNumber = (number, id, testingEnvironment)
break break
} }
@ -1195,12 +1189,13 @@ final class SharedApplicationContext {
} }
})) }))
self.logoutDisposable.set((self.sharedContextPromise.get()
let logoutDataSignal: Signal<(AccountManager, Set<PeerId>), NoError> = self.sharedContextPromise.get()
|> take(1) |> take(1)
|> mapToSignal { sharedContext -> Signal<(AccountManager, Set<PeerId>), NoError> in |> mapToSignal { sharedContext -> Signal<(AccountManager, Set<PeerId>), NoError> in
return sharedContext.sharedContext.activeAccounts return sharedContext.sharedContext.activeAccountContexts
|> map { _, accounts, _ -> Set<PeerId> in |> map { _, accounts, _ -> Set<PeerId> in
return Set(accounts.map { $0.1.peerId }) return Set(accounts.map { $0.1.account.peerId })
} }
|> reduceLeft(value: Set<PeerId>()) { current, updated, emit in |> reduceLeft(value: Set<PeerId>()) { current, updated, emit in
if !current.isEmpty { if !current.isEmpty {
@ -1211,7 +1206,9 @@ final class SharedApplicationContext {
|> map { loggedOutAccountPeerIds -> (AccountManager, Set<PeerId>) in |> map { loggedOutAccountPeerIds -> (AccountManager, Set<PeerId>) in
return (sharedContext.sharedContext.accountManager, loggedOutAccountPeerIds) return (sharedContext.sharedContext.accountManager, loggedOutAccountPeerIds)
} }
}).start(next: { accountManager, loggedOutAccountPeerIds in }
self.logoutDisposable.set(logoutDataSignal.start(next: { accountManager, loggedOutAccountPeerIds in
let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in
var updated = current var updated = current
for peerId in loggedOutAccountPeerIds { for peerId in loggedOutAccountPeerIds {
@ -1266,7 +1263,7 @@ final class SharedApplicationContext {
return activeAccountsAndPeers(context: context.context) return activeAccountsAndPeers(context: context.context)
|> take(1) |> take(1)
|> map { primaryAndAccounts -> (Account, Peer, Int32)? in |> map { primaryAndAccounts -> (AccountContext, Peer, Int32)? in
return primaryAndAccounts.1.first return primaryAndAccounts.1.first
} }
|> map { accountAndPeer -> String? in |> map { accountAndPeer -> String? in
@ -1579,13 +1576,13 @@ final class SharedApplicationContext {
encryptedPayload.append("=") encryptedPayload.append("=")
} }
if let data = Data(base64Encoded: encryptedPayload) { if let data = Data(base64Encoded: encryptedPayload) {
let _ = (sharedApplicationContext.sharedContext.activeAccounts let _ = (sharedApplicationContext.sharedContext.activeAccountContexts
|> take(1) |> take(1)
|> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in |> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in
return combineLatest(activeAccounts.accounts.map { account -> Signal<(Account, MasterNotificationKey), NoError> in return combineLatest(activeAccounts.accounts.map { context -> Signal<(Account, MasterNotificationKey), NoError> in
return masterNotificationsKey(account: account.1, ignoreDisabled: true) return masterNotificationsKey(account: context.1.account, ignoreDisabled: true)
|> map { key -> (Account, MasterNotificationKey) in |> map { key -> (Account, MasterNotificationKey) in
return (account.1, key) return (context.1.account, key)
} }
}) })
} }
@ -1803,38 +1800,38 @@ final class SharedApplicationContext {
let signal = self.sharedContextPromise.get() let signal = self.sharedContextPromise.get()
|> take(1) |> take(1)
|> mapToSignal { sharedApplicationContext -> Signal<(AccountRecordId?, [Account?]), NoError> in |> mapToSignal { sharedApplicationContext -> Signal<(AccountRecordId?, [AccountContext?]), NoError> in
return sharedApplicationContext.sharedContext.activeAccounts return sharedApplicationContext.sharedContext.activeAccountContexts
|> take(1) |> take(1)
|> mapToSignal { primary, accounts, _ -> Signal<(AccountRecordId?, [Account?]), NoError> in |> mapToSignal { primary, contexts, _ -> Signal<(AccountRecordId?, [AccountContext?]), NoError> in
return combineLatest(accounts.map { _, account, _ -> Signal<Account?, NoError> in return combineLatest(contexts.map { _, context, _ -> Signal<AccountContext?, NoError> in
return account.postbox.transaction { transaction -> Account? in return context.account.postbox.transaction { transaction -> AccountContext? in
if transaction.getPeer(peerId) != nil { if transaction.getPeer(peerId) != nil {
return account return context
} else { } else {
return nil return nil
} }
} }
}) })
|> map { accounts -> (AccountRecordId?, [Account?]) in |> map { contexts -> (AccountRecordId?, [AccountContext?]) in
return (primary?.id, accounts) return (primary?.account.id, contexts)
} }
} }
} }
let _ = (signal let _ = (signal
|> deliverOnMainQueue).start(next: { primary, accounts in |> deliverOnMainQueue).start(next: { primary, contexts in
if let primary = primary { if let primary = primary {
for account in accounts { for context in contexts {
if let account = account, account.id == primary { if let context = context, context.account.id == primary {
self.openChatWhenReady(accountId: nil, peerId: peerId) self.openChatWhenReady(accountId: nil, peerId: peerId)
return return
} }
} }
} }
for account in accounts { for context in contexts {
if let account = account { if let context = context {
self.openChatWhenReady(accountId: account.id, peerId: peerId) self.openChatWhenReady(accountId: context.account.id, peerId: peerId)
return return
} }
} }
@ -1974,11 +1971,11 @@ final class SharedApplicationContext {
|> deliverOnMainQueue |> deliverOnMainQueue
|> mapToSignal { sharedContext -> Signal<Void, NoError> in |> mapToSignal { sharedContext -> Signal<Void, NoError> in
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true) sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true)
return sharedContext.sharedContext.activeAccounts return sharedContext.sharedContext.activeAccountContexts
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in |> mapToSignal { _, contexts, _ -> Signal<Account, NoError> in
for account in accounts { for context in contexts {
if account.1.id == accountId { if context.1.account.id == accountId {
return .single(account.1) return .single(context.1.account)
} }
} }
return .complete() return .complete()
@ -2300,13 +2297,13 @@ private func accountIdFromNotification(_ notification: UNNotification, sharedCon
return sharedContext return sharedContext
|> take(1) |> take(1)
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in |> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
return sharedContext.sharedContext.activeAccounts return sharedContext.sharedContext.activeAccountContexts
|> take(1) |> take(1)
|> mapToSignal { _, accounts, _ -> Signal<AccountRecordId?, NoError> in |> mapToSignal { _, contexts, _ -> Signal<AccountRecordId?, NoError> in
let keys = accounts.map { _, account, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in let keys = contexts.map { _, context, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in
return masterNotificationsKey(account: account, ignoreDisabled: true) return masterNotificationsKey(account: context.account, ignoreDisabled: true)
|> map { key in |> map { key in
return (account.id, key) return (context.account.id, key)
} }
} }
return combineLatest(keys) return combineLatest(keys)
@ -2324,12 +2321,12 @@ private func accountIdFromNotification(_ notification: UNNotification, sharedCon
return sharedContext return sharedContext
|> take(1) |> take(1)
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in |> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
return sharedContext.sharedContext.activeAccounts return sharedContext.sharedContext.activeAccountContexts
|> take(1) |> take(1)
|> map { _, accounts, _ -> AccountRecordId? in |> map { _, contexts, _ -> AccountRecordId? in
for (_, account, _) in accounts { for (_, context, _) in contexts {
if Int(account.peerId.id._internalGetInt32Value()) == userId { if Int(context.account.peerId.id._internalGetInt32Value()) == userId {
return account.id return context.account.id
} }
} }
return nil return nil

View File

@ -870,21 +870,21 @@ final class AuthorizedApplicationContext {
func switchAccount() { func switchAccount() {
let _ = (activeAccountsAndPeers(context: self.context) let _ = (activeAccountsAndPeers(context: self.context)
|> take(1) |> take(1)
|> map { primaryAndAccounts -> (Account, Peer, Int32)? in |> map { primaryAndAccounts -> (AccountContext, Peer, Int32)? in
return primaryAndAccounts.1.first return primaryAndAccounts.1.first
} }
|> map { accountAndPeer -> Account? in |> map { accountAndPeer -> AccountContext? in
if let (account, _, _) = accountAndPeer { if let (context, _, _) = accountAndPeer {
return account return context
} else { } else {
return nil return nil
} }
} }
|> deliverOnMainQueue).start(next: { [weak self] account in |> deliverOnMainQueue).start(next: { [weak self] context in
guard let strongSelf = self, let account = account else { guard let strongSelf = self, let context = context else {
return return
} }
strongSelf.context.sharedContext.switchToAccount(id: account.id, fromSettingsController: nil, withChatListController: nil) strongSelf.context.sharedContext.switchToAccount(id: context.account.id, fromSettingsController: nil, withChatListController: nil)
}) })
} }

View File

@ -433,13 +433,13 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
private func refreshQrToken() { private func refreshQrToken() {
let sharedContext = self.sharedContext let sharedContext = self.sharedContext
let account = self.account let account = self.account
let tokenSignal = sharedContext.activeAccounts let tokenSignal = sharedContext.activeAccountContexts
|> castError(ExportAuthTransferTokenError.self) |> castError(ExportAuthTransferTokenError.self)
|> take(1) |> take(1)
|> mapToSignal { activeAccountsAndInfo -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in |> mapToSignal { activeAccountsAndInfo -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
let (_, activeAccounts, _) = activeAccountsAndInfo let (_, activeAccounts, _) = activeAccountsAndInfo
let activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id }) let activeProductionUserIds = activeAccounts.map({ $0.1.account }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id })
let activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id }) let activeTestingUserIds = activeAccounts.map({ $0.1.account }).filter({ $0.testingEnvironment }).map({ $0.peerId.id })
let allProductionUserIds = activeProductionUserIds let allProductionUserIds = activeProductionUserIds
let allTestingUserIds = activeTestingUserIds let allTestingUserIds = activeTestingUserIds

View File

@ -203,9 +203,9 @@ public final class NotificationViewControllerImpl {
return return
} }
self.applyDisposable.set((sharedAccountContext.activeAccounts self.applyDisposable.set((sharedAccountContext.activeAccountContexts
|> map { _, accounts, _ -> Account? in |> map { _, accounts, _ -> Account? in
return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1.account
} }
|> filter { account in |> filter { account in
return account != nil return account != nil
@ -254,16 +254,16 @@ public final class NotificationViewControllerImpl {
self.imageInfo = (true, dimensions.cgSize) self.imageInfo = (true, dimensions.cgSize)
self.updateImageLayout(boundingSize: view.bounds.size) self.updateImageLayout(boundingSize: view.bounds.size)
self.applyDisposable.set((sharedAccountContext.activeAccounts self.applyDisposable.set((sharedAccountContext.activeAccountContexts
|> map { _, accounts, _ -> Account? in |> map { _, contexts, _ -> AccountContext? in
return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 return contexts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1
} }
|> filter { account in |> filter { context in
return account != nil return context != nil
} }
|> take(1) |> take(1)
|> mapToSignal { account -> Signal<(Account, FileMediaReference?), NoError> in |> mapToSignal { context -> Signal<(Account, FileMediaReference?), NoError> in
guard let account = account else { guard let account = context?.account else {
return .complete() return .complete()
} }
return account.postbox.messageAtId(messageId) return account.postbox.messageAtId(messageId)

View File

@ -112,7 +112,7 @@ final class PeerInfoState {
final class TelegramGlobalSettings { final class TelegramGlobalSettings {
let suggestPhoneNumberConfirmation: Bool let suggestPhoneNumberConfirmation: Bool
let suggestPasswordConfirmation: Bool let suggestPasswordConfirmation: Bool
let accountsAndPeers: [(Account, Peer, Int32)] let accountsAndPeers: [(AccountContext, Peer, Int32)]
let activeSessionsContext: ActiveSessionsContext? let activeSessionsContext: ActiveSessionsContext?
let webSessionsContext: WebSessionsContext? let webSessionsContext: WebSessionsContext?
let otherSessionsCount: Int? let otherSessionsCount: Int?
@ -131,7 +131,7 @@ final class TelegramGlobalSettings {
init( init(
suggestPhoneNumberConfirmation: Bool, suggestPhoneNumberConfirmation: Bool,
suggestPasswordConfirmation: Bool, suggestPasswordConfirmation: Bool,
accountsAndPeers: [(Account, Peer, Int32)], accountsAndPeers: [(AccountContext, Peer, Int32)],
activeSessionsContext: ActiveSessionsContext?, activeSessionsContext: ActiveSessionsContext?,
webSessionsContext: WebSessionsContext?, webSessionsContext: WebSessionsContext?,
otherSessionsCount: Int?, otherSessionsCount: Int?,
@ -356,7 +356,7 @@ func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signa
} }
} }
func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> { func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(AccountContext, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
let preferences = context.sharedContext.accountManager.sharedData(keys: [ let preferences = context.sharedContext.accountManager.sharedData(keys: [
SharedDataKeys.proxySettings, SharedDataKeys.proxySettings,
ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.inAppNotificationSettings,

View File

@ -720,19 +720,19 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
} }
if !settings.accountsAndPeers.isEmpty { if !settings.accountsAndPeers.isEmpty {
for (peerAccount, peer, badgeCount) in settings.accountsAndPeers { for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers {
let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer)) let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer))
items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccount), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, action: { action in items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccountContext.account), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, action: { action in
switch action { switch action {
case .open: case .open:
interaction.switchToAccount(peerAccount.id) interaction.switchToAccount(peerAccountContext.account.id)
case .remove: case .remove:
interaction.logoutAccount(peerAccount.id) interaction.logoutAccount(peerAccountContext.account.id)
default: default:
break break
} }
}, contextAction: { node, gesture in }, contextAction: { node, gesture in
interaction.accountContextMenu(peerAccount.id, node, gesture) interaction.accountContextMenu(peerAccountContext.account.id, node, gesture)
})) }))
} }
if settings.accountsAndPeers.count + 1 < maximumNumberOfAccounts { if settings.accountsAndPeers.count + 1 < maximumNumberOfAccounts {
@ -1549,7 +1549,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
private let displayAsPeersPromise = Promise<[FoundPeer]>([]) private let displayAsPeersPromise = Promise<[FoundPeer]>([])
fileprivate let accountsAndPeers = Promise<[(Account, Peer, Int32)]>() fileprivate let accountsAndPeers = Promise<[(AccountContext, Peer, Int32)]>()
fileprivate let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>() fileprivate let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>()
private let notificationExceptions = Promise<NotificationExceptionsList?>() private let notificationExceptions = Promise<NotificationExceptionsList?>()
private let privacySettings = Promise<AccountPrivacySettings?>() private let privacySettings = Promise<AccountPrivacySettings?>()
@ -5559,8 +5559,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { accountsAndPeers in |> deliverOnMainQueue).start(next: { accountsAndPeers in
for (account, _, _) in accountsAndPeers { for (account, _, _) in accountsAndPeers {
if account.id == id { if account.account.id == id {
selectedAccount = account selectedAccount = account.account
break break
} }
} }
@ -6450,8 +6450,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let accountsAndPeers = Promise<((Account, Peer)?, [(Account, Peer, Int32)])>() private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>()
private var accountsAndPeersValue: ((Account, Peer)?, [(Account, Peer, Int32)])? private var accountsAndPeersValue: ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])?
private var accountsAndPeersDisposable: Disposable? private var accountsAndPeersDisposable: Disposable?
private let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>(nil) private let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>(nil)
@ -6545,7 +6545,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
let accountTabBarAvatar: Signal<(UIImage, UIImage)?, NoError> = combineLatest(self.accountsAndPeers.get(), context.sharedContext.presentationData) let accountTabBarAvatar: Signal<(UIImage, UIImage)?, NoError> = combineLatest(self.accountsAndPeers.get(), context.sharedContext.presentationData)
|> map { primaryAndOther, presentationData -> (Account, Peer, PresentationTheme)? in |> map { primaryAndOther, presentationData -> (Account, Peer, PresentationTheme)? in
if let primary = primaryAndOther.0, !primaryAndOther.1.isEmpty { if let primary = primaryAndOther.0, !primaryAndOther.1.isEmpty {
return (primary.0, primary.1, presentationData.theme) return (primary.0.account, primary.1, presentationData.theme)
} else { } else {
return nil return nil
} }
@ -6811,7 +6811,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
let avatarSize = CGSize(width: 28.0, height: 28.0) let avatarSize = CGSize(width: 28.0, height: 28.0)
items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0, peer: primary.1, size: avatarSize)), action: { _, f in items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0.account, peer: primary.1, size: avatarSize)), action: { _, f in
f(.default) f(.default)
}))) })))
@ -6820,8 +6820,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
} }
for account in other { for account in other {
let id = account.0.id let id = account.0.account.id
items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0, peer: account.1, size: avatarSize)), action: { [weak self] _, f in items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0.account, peer: account.1, size: avatarSize)), action: { [weak self] _, f in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -6830,7 +6830,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
}))) })))
} }
let controller = ContextController(account: primary.0, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture) let controller = ContextController(account: primary.0.account, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
} }
} }

View File

@ -32,7 +32,7 @@ private final class InternalContext {
init(sharedContext: SharedAccountContextImpl) { init(sharedContext: SharedAccountContextImpl) {
self.sharedContext = sharedContext self.sharedContext = sharedContext
self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
return sharedContext.accountUserInterfaceInUse(id) return sharedContext.accountUserInterfaceInUse(id)
}) })
} }

View File

@ -42,12 +42,12 @@ private struct AccountAttributes: Equatable {
private enum AddedAccountResult { private enum AddedAccountResult {
case upgrading(Float) case upgrading(Float)
case ready(AccountRecordId, Account?, Int32) case ready(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)
} }
private enum AddedAccountsResult { private enum AddedAccountsResult {
case upgrading(Float) case upgrading(Float)
case ready([(AccountRecordId, Account?, Int32)]) case ready([(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)])
} }
private var testHasInstance = false private var testHasInstance = false
@ -65,9 +65,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private let apsNotificationToken: Signal<Data?, NoError> private let apsNotificationToken: Signal<Data?, NoError>
private let voipNotificationToken: Signal<Data?, NoError> private let voipNotificationToken: Signal<Data?, NoError>
private var activeAccountsValue: (primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?)? private var activeAccountsValue: (primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?)?
private let activeAccountsPromise = Promise<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?)>() private let activeAccountsPromise = Promise<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?)>()
public var activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?), NoError> { public var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> {
return self.activeAccountsPromise.get() return self.activeAccountsPromise.get()
} }
private let managedAccountDisposables = DisposableDict<AccountRecordId>() private let managedAccountDisposables = DisposableDict<AccountRecordId>()
@ -401,17 +401,22 @@ public final class SharedAccountContextImpl: SharedAccountContext {
for (id, attributes) in records { for (id, attributes) in records {
if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil { if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil {
addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: telegramAccountAuxiliaryMethods) addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: telegramAccountAuxiliaryMethods)
|> map { result -> AddedAccountResult in |> mapToSignal { result -> Signal<AddedAccountResult, NoError> in
switch result { switch result {
case let .authorized(account): case let .authorized(account):
setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia, preFetchedResourcePath: { resource in setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia, preFetchedResourcePath: { resource in
return nil return nil
}) })
return .ready(id, account, attributes.sortIndex) return account.postbox.transaction { transaction -> AddedAccountResult in
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
let contentSettings = getContentSettings(transaction: transaction)
let appConfiguration = getAppConfiguration(transaction: transaction)
return .ready(id, account, attributes.sortIndex, limitsConfiguration, contentSettings, appConfiguration)
}
case let .upgrading(progress): case let .upgrading(progress):
return .upgrading(progress) return .single(.upgrading(progress))
default: default:
return .ready(id, nil, attributes.sortIndex) return .single(.ready(id, nil, attributes.sortIndex, nil, nil, nil))
} }
}) })
} }
@ -432,13 +437,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
let mappedAddedAccounts = combineLatest(queue: .mainQueue(), addedSignals) let mappedAddedAccounts = combineLatest(queue: .mainQueue(), addedSignals)
|> map { results -> AddedAccountsResult in |> map { results -> AddedAccountsResult in
var readyAccounts: [(AccountRecordId, Account?, Int32)] = [] var readyAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = []
var totalProgress: Float = 0.0 var totalProgress: Float = 0.0
var hasItemsWithProgress = false var hasItemsWithProgress = false
for result in results { for result in results {
switch result { switch result {
case let .ready(id, account, sortIndex): case let .ready(id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration):
readyAccounts.append((id, account, sortIndex)) readyAccounts.append((id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration))
totalProgress += 1.0 totalProgress += 1.0
case let .upgrading(progress): case let .upgrading(progress):
hasItemsWithProgress = true hasItemsWithProgress = true
@ -456,7 +461,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|> deliverOnMainQueue).start(next: { mappedAddedAccounts, authAccount in |> deliverOnMainQueue).start(next: { mappedAddedAccounts, authAccount in
print("SharedAccountContextImpl: accounts processed in \(CFAbsoluteTimeGetCurrent() - startTime)") print("SharedAccountContextImpl: accounts processed in \(CFAbsoluteTimeGetCurrent() - startTime)")
var addedAccounts: [(AccountRecordId, Account?, Int32)] = [] var addedAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = []
switch mappedAddedAccounts { switch mappedAddedAccounts {
case let .upgrading(progress): case let .upgrading(progress):
self.displayUpgradeProgress(progress) self.displayUpgradeProgress(progress)
@ -494,7 +499,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.managedAccountDisposables.set(nil, forKey: account.id) self.managedAccountDisposables.set(nil, forKey: account.id)
assertionFailure() assertionFailure()
} }
self.activeAccountsValue!.accounts.append((account.id, account, accountRecord.2))
let context = AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: accountRecord.3 ?? .defaultValue, contentSettings: accountRecord.4 ?? .default, appConfiguration: accountRecord.5 ?? .defaultValue)
self.activeAccountsValue!.accounts.append((account.id, context, accountRecord.2))
self.managedAccountDisposables.set(self.updateAccountBackupData(account: account).start(), forKey: account.id) self.managedAccountDisposables.set(self.updateAccountBackupData(account: account).start(), forKey: account.id)
account.resetStateManagement() account.resetStateManagement()
hadUpdates = true hadUpdates = true
@ -520,7 +529,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.managedAccountDisposables.set(nil, forKey: id) self.managedAccountDisposables.set(nil, forKey: id)
} }
} }
var primary: Account? var primary: AccountContext?
if let primaryId = primaryId { if let primaryId = primaryId {
if let index = self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == primaryId }) { if let index = self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == primaryId }) {
primary = self.activeAccountsValue?.accounts[index].1 primary = self.activeAccountsValue?.accounts[index].1
@ -531,8 +540,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
if primary !== self.activeAccountsValue!.primary { if primary !== self.activeAccountsValue!.primary {
hadUpdates = true hadUpdates = true
self.activeAccountsValue!.primary?.postbox.clearCaches() self.activeAccountsValue!.primary?.account.postbox.clearCaches()
self.activeAccountsValue!.primary?.resetCachedData() self.activeAccountsValue!.primary?.account.resetCachedData()
self.activeAccountsValue!.primary = primary self.activeAccountsValue!.primary = primary
} }
if self.activeAccountsValue!.currentAuth?.id != authRecord?.0 { if self.activeAccountsValue!.currentAuth?.id != authRecord?.0 {
@ -555,16 +564,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
})) }))
}) })
self.activeAccountsWithInfoPromise.set(self.activeAccounts self.activeAccountsWithInfoPromise.set(self.activeAccountContexts
|> mapToSignal { primary, accounts, _ -> Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> in |> mapToSignal { primary, accounts, _ -> Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> in
return combineLatest(accounts.map { _, account, _ -> Signal<AccountWithInfo?, NoError> in return combineLatest(accounts.map { _, context, _ -> Signal<AccountWithInfo?, NoError> in
let peerViewKey: PostboxViewKey = .peer(peerId: account.peerId, components: []) let peerViewKey: PostboxViewKey = .peer(peerId: context.account.peerId, components: [])
return account.postbox.combinedView(keys: [peerViewKey]) return context.account.postbox.combinedView(keys: [peerViewKey])
|> map { view -> AccountWithInfo? in |> map { view -> AccountWithInfo? in
guard let peerView = view.views[peerViewKey] as? PeerView, let peer = peerView.peers[peerView.peerId] else { guard let peerView = view.views[peerViewKey] as? PeerView, let peer = peerView.peers[peerView.peerId] else {
return nil return nil
} }
return AccountWithInfo(account: account, peer: peer) return AccountWithInfo(account: context.account, peer: peer)
} }
|> distinctUntilChanged |> distinctUntilChanged
}) })
@ -575,7 +584,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
accountsWithInfoResult.append(info) accountsWithInfoResult.append(info)
} }
} }
return (primary?.id, accountsWithInfoResult) return (primary?.account.id, accountsWithInfoResult)
} }
}) })
@ -604,7 +613,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return return
} }
strongSelf.mediaManager.playlistControl(.playback(.play), type: nil) strongSelf.mediaManager.playlistControl(.playback(.play), type: nil)
}, audioSession: self.mediaManager.audioSession, activeAccounts: self.activeAccounts |> map { _, accounts, _ in }, audioSession: self.mediaManager.audioSession, activeAccounts: self.activeAccountContexts |> map { _, accounts, _ in
return Array(accounts.map({ $0.1 })) return Array(accounts.map({ $0.1 }))
}) })
self.callManager = callManager self.callManager = callManager
@ -619,7 +628,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
if let call = call { if let call = call {
mainWindow.hostView.containerView.endEditing(true) mainWindow.hostView.containerView.endEditing(true)
let callController = CallController(sharedContext: strongSelf, account: call.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild) let callController = CallController(sharedContext: strongSelf, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
strongSelf.callController = callController strongSelf.callController = callController
strongSelf.mainWindow?.present(callController, on: .calls) strongSelf.mainWindow?.present(callController, on: .calls)
strongSelf.callState.set(call.state strongSelf.callState.set(call.state
@ -686,7 +695,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
if let strongSelf = self { if let strongSelf = self {
let statusBarContent: CallStatusBarNodeImpl.Content? let statusBarContent: CallStatusBarNodeImpl.Content?
if let call = call { if let call = call {
statusBarContent = .call(strongSelf, call.account, call) statusBarContent = .call(strongSelf, call.context.account, call)
} else if let groupCall = groupCall, !hasGroupCallOnScreen { } else if let groupCall = groupCall, !hasGroupCallOnScreen {
statusBarContent = .groupCall(strongSelf, groupCall.account, groupCall) statusBarContent = .groupCall(strongSelf, groupCall.account, groupCall)
} else { } else {
@ -748,9 +757,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.updateNotificationTokensRegistration() self.updateNotificationTokensRegistration()
if applicationBindings.isMainApp { if applicationBindings.isMainApp {
self.widgetDataContext = WidgetDataContext(basePath: self.basePath, inForeground: self.applicationBindings.applicationInForeground, activeAccounts: self.activeAccounts self.widgetDataContext = WidgetDataContext(basePath: self.basePath, inForeground: self.applicationBindings.applicationInForeground, activeAccounts: self.activeAccountContexts
|> map { _, accounts, _ in |> map { _, accounts, _ in
return accounts.map { $0.1 } return accounts.map { $0.1.account }
}, presentationData: self.presentationData, appLockContext: self.appLockContext as! AppLockContextImpl) }, presentationData: self.presentationData, appLockContext: self.appLockContext as! AppLockContextImpl)
let enableSpotlight = accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.intentsSettings])) let enableSpotlight = accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.intentsSettings]))
@ -759,10 +768,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return intentsSettings.contacts return intentsSettings.contacts
} }
|> distinctUntilChanged |> distinctUntilChanged
self.spotlightDataContext = SpotlightDataContext(appBasePath: applicationBindings.containerPath, accountManager: accountManager, accounts: combineLatest(enableSpotlight, self.activeAccounts self.spotlightDataContext = SpotlightDataContext(appBasePath: applicationBindings.containerPath, accountManager: accountManager, accounts: combineLatest(enableSpotlight, self.activeAccountContexts
|> map { _, accounts, _ in |> map { _, accounts, _ in
return accounts.map { _, account, _ in return accounts.map { _, account, _ in
return account return account.account
} }
}) |> map { enableSpotlight, accounts in }) |> map { enableSpotlight, accounts in
if enableSpotlight { if enableSpotlight {
@ -830,24 +839,24 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return true return true
}) })
self.registeredNotificationTokensDisposable.set((combineLatest(queue: .mainQueue(), settings, self.activeAccounts) self.registeredNotificationTokensDisposable.set((combineLatest(queue: .mainQueue(), settings, self.activeAccountContexts)
|> mapToSignal { settings, activeAccountsAndInfo -> Signal<Never, NoError> in |> mapToSignal { settings, activeAccountsAndInfo -> Signal<Never, NoError> in
let (primary, activeAccounts, _) = activeAccountsAndInfo let (primary, activeAccounts, _) = activeAccountsAndInfo
var applied: [Signal<Never, NoError>] = [] var applied: [Signal<Never, NoError>] = []
var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id }) var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.account.testingEnvironment }).map({ $0.account.peerId.id })
var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id }) var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.account.testingEnvironment }).map({ $0.account.peerId.id })
let allProductionUserIds = activeProductionUserIds let allProductionUserIds = activeProductionUserIds
let allTestingUserIds = activeTestingUserIds let allTestingUserIds = activeTestingUserIds
if !settings.allAccounts { if !settings.allAccounts {
if let primary = primary { if let primary = primary {
if !primary.testingEnvironment { if !primary.account.testingEnvironment {
activeProductionUserIds = [primary.peerId.id] activeProductionUserIds = [primary.account.peerId.id]
activeTestingUserIds = [] activeTestingUserIds = []
} else { } else {
activeProductionUserIds = [] activeProductionUserIds = []
activeTestingUserIds = [primary.peerId.id] activeTestingUserIds = [primary.account.peerId.id]
} }
} else { } else {
activeProductionUserIds = [] activeProductionUserIds = []
@ -859,14 +868,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
let appliedAps: Signal<Never, NoError> let appliedAps: Signal<Never, NoError>
let appliedVoip: Signal<Never, NoError> let appliedVoip: Signal<Never, NoError>
if !activeProductionUserIds.contains(account.peerId.id) && !activeTestingUserIds.contains(account.peerId.id) { if !activeProductionUserIds.contains(account.account.peerId.id) && !activeTestingUserIds.contains(account.account.peerId.id) {
appliedAps = self.apsNotificationToken appliedAps = self.apsNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 }) |> distinctUntilChanged(isEqual: { $0 == $1 })
|> mapToSignal { token -> Signal<Never, NoError> in |> mapToSignal { token -> Signal<Never, NoError> in
guard let token = token else { guard let token = token else {
return .complete() return .complete()
} }
return TelegramEngine(account: account).accountData.unregisterNotificationToken(token: token, type: .aps(encrypt: false), otherAccountUserIds: (account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.peerId.id })) return account.engine.accountData.unregisterNotificationToken(token: token, type: .aps(encrypt: false), otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
} }
appliedVoip = self.voipNotificationToken appliedVoip = self.voipNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 }) |> distinctUntilChanged(isEqual: { $0 == $1 })
@ -874,7 +883,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
guard let token = token else { guard let token = token else {
return .complete() return .complete()
} }
return TelegramEngine(account: account).accountData.unregisterNotificationToken(token: token, type: .voip, otherAccountUserIds: (account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.peerId.id })) return account.engine.accountData.unregisterNotificationToken(token: token, type: .voip, otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
} }
} else { } else {
appliedAps = self.apsNotificationToken appliedAps = self.apsNotificationToken
@ -889,7 +898,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} else { } else {
encrypt = false encrypt = false
} }
return TelegramEngine(account: account).accountData.registerNotificationToken(token: token, type: .aps(encrypt: encrypt), sandbox: sandbox, otherAccountUserIds: (account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.peerId.id }), excludeMutedChats: !settings.includeMuted) return account.engine.accountData.registerNotificationToken(token: token, type: .aps(encrypt: encrypt), sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
} }
appliedVoip = self.voipNotificationToken appliedVoip = self.voipNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 }) |> distinctUntilChanged(isEqual: { $0 == $1 })
@ -897,7 +906,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
guard let token = token else { guard let token = token else {
return .complete() return .complete()
} }
return TelegramEngine(account: account).accountData.registerNotificationToken(token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: (account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.peerId.id }), excludeMutedChats: !settings.includeMuted) return account.engine.accountData.registerNotificationToken(token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
} }
} }
@ -916,7 +925,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
public func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: ViewController? = nil, withChatListController chatListController: ViewController? = nil) { public func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: ViewController? = nil, withChatListController chatListController: ViewController? = nil) {
if self.activeAccountsValue?.primary?.id == id { if self.activeAccountsValue?.primary?.account.id == id {
return return
} }

View File

@ -129,7 +129,7 @@ public final class SharedWakeupManager {
let hasActiveCalls = (callManager?.currentCallSignal ?? .single(nil)) let hasActiveCalls = (callManager?.currentCallSignal ?? .single(nil))
|> map { call in |> map { call in
return call?.account.id == account.id return call?.context.account.id == account.id
} }
|> distinctUntilChanged |> distinctUntilChanged

View File

@ -107,11 +107,13 @@ private final class FdReadConnection {
break break
} else { } else {
assert(bytesRead == 4) assert(bytesRead == 4)
if length > 0 {
assert(length > 0 && length <= 30 * 1024 * 1024) assert(length > 0 && length <= 30 * 1024 * 1024)
strongSelf.currendData = PendingData(count: Int(length)) strongSelf.currendData = PendingData(count: Int(length))
} }
} }
} }
}
}) })
self.channel.resume() self.channel.resume()
} }
@ -513,6 +515,7 @@ public final class IpcGroupCallBufferBroadcastContext {
case callEnded case callEnded
case error case error
} }
case active
case finished(FinishReason) case finished(FinishReason)
} }
@ -620,6 +623,8 @@ public final class IpcGroupCallBufferBroadcastContext {
}, queue: .mainQueue()) }, queue: .mainQueue())
self.keepaliveInfoTimer = keepaliveInfoTimer self.keepaliveInfoTimer = keepaliveInfoTimer
keepaliveInfoTimer.start() keepaliveInfoTimer.start()
self.statusPromise.set(.single(.active))
} }
} }

View File

@ -252,20 +252,7 @@ private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> Ongo
} }
} }
/*private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { private protocol OngoingCallThreadLocalContextProtocol: AnyObject {
switch type {
case .never:
return .never
case .cellular:
return .cellular
case .always:
return .always
default:
return .never
}
}*/
private protocol OngoingCallThreadLocalContextProtocol: class {
func nativeSetNetworkType(_ type: NetworkType) func nativeSetNetworkType(_ type: NetworkType)
func nativeSetIsMuted(_ value: Bool) func nativeSetIsMuted(_ value: Bool)
func nativeSetIsLowBatteryLevel(_ value: Bool) func nativeSetIsLowBatteryLevel(_ value: Bool)
@ -277,6 +264,7 @@ private protocol OngoingCallThreadLocalContextProtocol: class {
func nativeDebugInfo() -> String func nativeDebugInfo() -> String
func nativeVersion() -> String func nativeVersion() -> String
func nativeGetDerivedState() -> Data func nativeGetDerivedState() -> Data
func addExternalAudioData(data: Data)
} }
private final class OngoingCallThreadLocalContextHolder { private final class OngoingCallThreadLocalContextHolder {
@ -329,6 +317,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
func nativeGetDerivedState() -> Data { func nativeGetDerivedState() -> Data {
return self.getDerivedState() return self.getDerivedState()
} }
func addExternalAudioData(data: Data) {
}
} }
public final class OngoingCallVideoCapturer { public final class OngoingCallVideoCapturer {
@ -517,6 +508,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
func nativeGetDerivedState() -> Data { func nativeGetDerivedState() -> Data {
return self.getDerivedState() return self.getDerivedState()
} }
func addExternalAudioData(data: Data) {
self.addExternalAudioData(data)
}
} }
private extension OngoingCallContextState.State { private extension OngoingCallContextState.State {
@ -586,8 +581,6 @@ extension OngoingCallVideoOrientation {
return .orientation180 return .orientation180
case .rotation270: case .rotation270:
return .orientation270 return .orientation270
@unknown default:
return .orientation0
} }
} }
} }
@ -1026,4 +1019,10 @@ public final class OngoingCallContext {
} }
} }
} }
public func addExternalAudioData(data: Data) {
self.withContext { context in
context.addExternalAudioData(data: data)
}
}
} }

View File

@ -221,6 +221,8 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
- (void)addSignalingData:(NSData * _Nonnull)data; - (void)addSignalingData:(NSData * _Nonnull)data;
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId; - (void)switchAudioOutput:(NSString * _Nonnull)deviceId;
- (void)switchAudioInput:(NSString * _Nonnull)deviceId; - (void)switchAudioInput:(NSString * _Nonnull)deviceId;
- (void)addExternalAudioData:(NSData * _Nonnull)data;
@end @end
typedef struct { typedef struct {

View File

@ -1264,6 +1264,15 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
_tgVoip->setAudioInputDevice(deviceId.UTF8String); _tgVoip->setAudioInputDevice(deviceId.UTF8String);
} }
- (void)addExternalAudioData:(NSData * _Nonnull)data {
if (_tgVoip) {
std::vector<uint8_t> samples;
samples.resize(data.length);
[data getBytes:samples.data() length:data.length];
_tgVoip->addExternalAudioSamples(std::move(samples));
}
}
@end @end
namespace { namespace {

@ -1 +1 @@
Subproject commit ef796349808b187b80b75ea1876b940f2882fcbb Subproject commit b2bc0201b3ae33982777c2d1ddbbc492c11b0625