From c86ceafb5c6755065f67c8130856349a3e4414b7 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Nov 2020 22:32:45 +0400 Subject: [PATCH 1/6] [WIP] Voice chats --- .../Sources/PresentationCallManager.swift | 1 + submodules/AudioBlob/BUILD | 17 +++ .../Sources/BlobView.swift | 17 ++- submodules/ItemListPeerItem/BUILD | 1 + .../Sources/ItemListPeerItem.swift | 62 ++++++++++- .../FFMpegMediaFrameSourceContext.swift | 4 +- submodules/TelegramApi/Sources/Api0.swift | 3 +- submodules/TelegramApi/Sources/Api1.swift | 26 +++++ submodules/TelegramApi/Sources/Api3.swift | 15 +++ .../Sources/PresentationGroupCall.swift | 105 ++++++++++++++++-- .../Sources/VoiceChatController.swift | 44 +++++++- .../TelegramCore/Sources/GroupCalls.swift | 76 ++++++++++++- .../Sources/StoreMessage_Telegram.swift | 2 +- .../Sources/TelegramMediaAction.swift | 2 + submodules/TelegramUI/BUILD | 1 + .../TelegramUI/Sources/ChatController.swift | 9 +- .../ChatMessageInteractiveFileNode.swift | 1 + .../ChatPanelInterfaceInteraction.swift | 2 +- .../Sources/ChatRecentActionsController.swift | 2 +- .../ChatTextInputMediaRecordingButton.swift | 1 + .../Sources/PeerInfo/PeerInfoScreen.swift | 2 +- .../Sources/GroupCallContext.swift | 16 ++- .../OngoingCallThreadLocalContext.h | 2 + .../Sources/OngoingCallThreadLocalContext.mm | 10 +- 24 files changed, 372 insertions(+), 49 deletions(-) create mode 100644 submodules/AudioBlob/BUILD rename submodules/{TelegramUI => AudioBlob}/Sources/BlobView.swift (97%) diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index d88f8a329a..431d10c532 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -198,6 +198,7 @@ public protocol PresentationGroupCall: class { var canBeRemoved: Signal { get } var state: Signal { get } var members: Signal<[PeerId: PresentationGroupCallMemberState], NoError> { get } + var audioLevels: Signal<[(PeerId, Float)], NoError> { get } func leave() -> Signal diff --git a/submodules/AudioBlob/BUILD b/submodules/AudioBlob/BUILD new file mode 100644 index 0000000000..4e0c55ac05 --- /dev/null +++ b/submodules/AudioBlob/BUILD @@ -0,0 +1,17 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AudioBlob", + module_name = "AudioBlob", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/LegacyComponents:LegacyComponents", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/BlobView.swift b/submodules/AudioBlob/Sources/BlobView.swift similarity index 97% rename from submodules/TelegramUI/Sources/BlobView.swift rename to submodules/AudioBlob/Sources/BlobView.swift index 4c62a3741a..8d753361e2 100644 --- a/submodules/TelegramUI/Sources/BlobView.swift +++ b/submodules/AudioBlob/Sources/BlobView.swift @@ -3,8 +3,7 @@ import UIKit import Display import LegacyComponents -final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration { - +public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration { private let smallBlob: BlobView private let mediumBlob: BlobView private let bigBlob: BlobView @@ -18,9 +17,9 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration private(set) var isAnimating = false - typealias BlobRange = (min: CGFloat, max: CGFloat) + public typealias BlobRange = (min: CGFloat, max: CGFloat) - init( + public init( frame: CGRect, maxLevel: CGFloat, smallBlobRange: BlobRange, @@ -84,13 +83,13 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration fatalError("init(coder:) has not been implemented") } - func setColor(_ color: UIColor) { + public func setColor(_ color: UIColor) { smallBlob.setColor(color) mediumBlob.setColor(color.withAlphaComponent(0.3)) bigBlob.setColor(color.withAlphaComponent(0.15)) } - func updateLevel(_ level: CGFloat) { + public func updateLevel(_ level: CGFloat) { let normalizedLevel = min(1, max(level / maxLevel, 0)) smallBlob.updateSpeedLevel(to: normalizedLevel) @@ -100,7 +99,7 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration audioLevel = normalizedLevel } - func startAnimating() { + public func startAnimating() { guard !isAnimating else { return } isAnimating = true @@ -112,7 +111,7 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration displayLinkAnimator?.isPaused = false } - func stopAnimating() { + public func stopAnimating() { guard isAnimating else { return } isAnimating = false @@ -138,7 +137,7 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration } } - override func layoutSubviews() { + override public func layoutSubviews() { super.layoutSubviews() smallBlob.frame = bounds diff --git a/submodules/ItemListPeerItem/BUILD b/submodules/ItemListPeerItem/BUILD index 804ffb8a2f..9828730878 100644 --- a/submodules/ItemListPeerItem/BUILD +++ b/submodules/ItemListPeerItem/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/ContextUI:ContextUI", "//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/AccountContext:AccountContext", + "//submodules/AudioBlob:AudioBlob", ], visibility = [ "//visibility:public", diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 7a9036e188..fa3b5e1a3d 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -15,6 +15,7 @@ import TelegramStringFormatting import PeerPresenceStatusManager import ContextUI import AccountContext +import AudioBlob private final class ShimmerEffectNode: ASDisplayNode { private var currentBackgroundColor: UIColor? @@ -346,8 +347,9 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { let shimmering: ItemListPeerItemShimmering? let displayDecorations: Bool let disableInteractiveTransitionIfNecessary: Bool + let audioLevel: Signal? - public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) { + public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false, audioLevel: Signal? = nil) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -380,6 +382,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.shimmering = shimmering self.displayDecorations = displayDecorations self.disableInteractiveTransitionIfNecessary = disableInteractiveTransitionIfNecessary + self.audioLevel = audioLevel } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -452,6 +455,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo private let containerNode: ContextControllerSourceNode + private var audioLevelView: VoiceBlobView? fileprivate let avatarNode: AvatarNode private let titleNode: TextNode private let labelNode: TextNode @@ -470,6 +474,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo private var editableControlNode: ItemListEditableControlNode? private var reorderControlNode: ItemListEditableReorderControlNode? + private let audioLevelDisposable = MetaDisposable() + override public var canBeSelected: Bool { if self.editableControlNode != nil || self.disabledOverlayNode != nil { return false @@ -499,7 +505,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo self.containerNode = ContextControllerSourceNode() self.avatarNode = AvatarNode(font: avatarFont) - self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() + //self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false @@ -550,6 +556,10 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo } } + deinit { + self.audioLevelDisposable.dispose() + } + override public func didLoad() { super.didLoad() @@ -1103,7 +1113,53 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth, y: labelFrame.minY - 1.0), size: CGSize(width: badgeWidth, height: badgeDiameter)) - transition.updateFrame(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))) + let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) + + let blobFrame = avatarFrame.insetBy(dx: -12.0, dy: -12.0) + + if let audioLevel = item.audioLevel { + strongSelf.audioLevelView?.frame = blobFrame + strongSelf.audioLevelDisposable.set((audioLevel + |> deliverOnMainQueue).start(next: { value in + guard let strongSelf = self else { + return + } + + if strongSelf.audioLevelView == nil { + let audioLevelView = VoiceBlobView( + frame: blobFrame, + maxLevel: 0.3, + smallBlobRange: (0, 0), + mediumBlobRange: (0.7, 0.8), + bigBlobRange: (0.8, 0.9) + ) + + let maskRect = CGRect(origin: .zero, size: blobFrame.size) + let playbackMaskLayer = CAShapeLayer() + playbackMaskLayer.frame = maskRect + playbackMaskLayer.fillRule = .evenOdd + let maskPath = UIBezierPath() + maskPath.append(UIBezierPath(roundedRect: maskRect.insetBy(dx: 12, dy: 12), cornerRadius: 22)) + maskPath.append(UIBezierPath(rect: maskRect)) + playbackMaskLayer.path = maskPath.cgPath + audioLevelView.layer.mask = playbackMaskLayer + + audioLevelView.setColor(.green) + strongSelf.audioLevelView = audioLevelView + strongSelf.containerNode.view.insertSubview(audioLevelView, at: 0) + audioLevelView.startAnimating() + } + + strongSelf.audioLevelView?.updateLevel(CGFloat(value) * 2.0) + })) + } else if let audioLevelView = strongSelf.audioLevelView { + strongSelf.audioLevelView = nil + audioLevelView.removeFromSuperview() + + strongSelf.audioLevelDisposable.set(nil) + } + + transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame) if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling { strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift index 378c76240c..713b0ae6ca 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift @@ -179,10 +179,10 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa } } if let fetchedData = fetchedData { - precondition(fetchedData.count <= readCount) + assert(fetchedData.count <= readCount) fetchedData.withUnsafeBytes { bytes -> Void in precondition(bytes.baseAddress != nil) - memcpy(buffer, bytes.baseAddress, fetchedData.count) + memcpy(buffer, bytes.baseAddress, min(fetchedData.count, readCount)) } fetchedCount = Int32(fetchedData.count) context.readingOffset += Int(fetchedCount) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 635d25785f..dc4989ab65 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -787,6 +787,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-202219658] = { return Api.MessageAction.parse_messageActionContactSignUp($0) } dict[-1730095465] = { return Api.MessageAction.parse_messageActionGeoProximityReached($0) } dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) } + dict[254144570] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) } dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) } dict[462375633] = { return Api.PhoneCall.parse_phoneCallWaiting($0) } dict[-2014659757] = { return Api.PhoneCall.parse_phoneCallRequested($0) } @@ -880,7 +881,7 @@ public struct Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index cf6dfdd91b..5a3022bcde 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -21479,6 +21479,7 @@ public extension Api { case messageActionContactSignUp case messageActionGeoProximityReached(fromId: Api.Peer, toId: Api.Peer, distance: Int32) case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?) + case messageActionInviteToGroupCall(call: Api.InputGroupCall, userId: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -21666,6 +21667,13 @@ public extension Api { call.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} break + case .messageActionInviteToGroupCall(let call, let userId): + if boxed { + buffer.appendInt32(254144570) + } + call.serialize(buffer, true) + serializeInt32(userId, buffer: buffer, boxed: false) + break } } @@ -21721,6 +21729,8 @@ public extension Api { return ("messageActionGeoProximityReached", [("fromId", fromId), ("toId", toId), ("distance", distance)]) case .messageActionGroupCall(let flags, let call, let duration): return ("messageActionGroupCall", [("flags", flags), ("call", call), ("duration", duration)]) + case .messageActionInviteToGroupCall(let call, let userId): + return ("messageActionInviteToGroupCall", [("call", call), ("userId", userId)]) } } @@ -22029,6 +22039,22 @@ public extension Api { return nil } } + public static func parse_messageActionInviteToGroupCall(_ reader: BufferReader) -> MessageAction? { + var _1: Api.InputGroupCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.MessageAction.messageActionInviteToGroupCall(call: _1!, userId: _2!) + } + else { + return nil + } + } } public enum PhoneCall: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 0639295a6c..c096955a8a 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -7310,6 +7310,21 @@ public extension Api { }) } + public static func inviteToGroupCall(call: Api.InputGroupCall, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-580284540) + call.serialize(buffer, true) + userId.serialize(buffer, true) + return (FunctionDescription(name: "phone.inviteToGroupCall", parameters: [("call", call), ("userId", userId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func discardGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(2054648117) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 5ad3be6624..06583f0b6c 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -27,7 +27,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private enum InternalState { case requesting case active(GroupCallInfo) - case estabilished(GroupCallInfo, String, [UInt32], [UInt32: PeerId]) + case estabilished(GroupCallInfo, String, UInt32, [UInt32], [UInt32: PeerId]) var callInfo: GroupCallInfo? { switch self { @@ -35,7 +35,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return nil case let .active(info): return info - case let .estabilished(info, _, _, _): + case let .estabilished(info, _, _, _, _): return info } } @@ -75,6 +75,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return self.audioOutputStatePromise.get() } + private let audioLevelsPipe = ValuePipe<[(PeerId, Float)]>() + public var audioLevels: Signal<[(PeerId, Float)], NoError> { + return self.audioLevelsPipe.signal() + } + private var audioLevelsDisposable = MetaDisposable() + private var audioSessionControl: ManagedAudioSessionControl? private var audioSessionDisposable: Disposable? private let audioSessionShouldBeActive = ValuePromise(false, ignoreRepeated: true) @@ -120,6 +126,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let memberStatesDisposable = MetaDisposable() private let leaveDisposable = MetaDisposable() + private var checkCallDisposable: Disposable? + private var isCurrentlyConnecting: Bool? + init( accountContext: AccountContext, audioSession: ManagedAudioSession, @@ -224,7 +233,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { guard let strongSelf = self else { return } - if case let .estabilished(callInfo, _, _, _) = strongSelf.internalState { + if case let .estabilished(callInfo, _, _, _, _) = strongSelf.internalState { var addedSsrc: [UInt32] = [] var removedSsrc: [UInt32] = [] for (callId, peerId, ssrc, isAdded) in updates { @@ -259,6 +268,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.isMutedDisposable.dispose() self.memberStatesDisposable.dispose() self.networkStateDisposable.dispose() + self.checkCallDisposable?.dispose() + self.audioLevelsDisposable.dispose() } private func updateSessionState(internalState: InternalState, audioSessionControl: ManagedAudioSessionControl?) { @@ -275,6 +286,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.audioSessionShouldBeActive.set(true) + switch previousInternalState { + case .requesting: + break + default: + if case .requesting = internalState { + self.isCurrentlyConnecting = nil + } + } + switch previousInternalState { case .active: break @@ -284,7 +304,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.callContext = callContext self.requestDisposable.set((callContext.joinPayload |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] joinPayload in + |> deliverOnMainQueue).start(next: { [weak self] joinPayload, ssrc in guard let strongSelf = self else { return } @@ -299,7 +319,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return } if let clientParams = joinCallResult.callInfo.clientParams { - strongSelf.updateSessionState(internalState: .estabilished(joinCallResult.callInfo, clientParams, joinCallResult.ssrcs, joinCallResult.ssrcMapping), audioSessionControl: strongSelf.audioSessionControl) + strongSelf.updateSessionState(internalState: .estabilished(joinCallResult.callInfo, clientParams, ssrc, joinCallResult.ssrcs, joinCallResult.ssrcMapping), audioSessionControl: strongSelf.audioSessionControl) } })) })) @@ -324,7 +344,21 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { case .connected: mappedState = .connected } - strongSelf.stateValue.networkState = mappedState + if strongSelf.stateValue.networkState != mappedState { + strongSelf.stateValue.networkState = mappedState + } + + let isConnecting = mappedState == .connecting + + if strongSelf.isCurrentlyConnecting != isConnecting { + strongSelf.isCurrentlyConnecting = isConnecting + if isConnecting { + strongSelf.startCheckingCallIfNeeded() + } else { + strongSelf.checkCallDisposable?.dispose() + strongSelf.checkCallDisposable = nil + } + } })) self.memberStatesDisposable.set((callContext.memberStates @@ -343,6 +377,22 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } strongSelf.membersValue = result })) + + self.audioLevelsDisposable.set((callContext.audioLevels + |> deliverOnMainQueue).start(next: { [weak self] levels in + guard let strongSelf = self else { + return + } + var result: [(PeerId, Float)] = [] + for (ssrc, level) in levels { + if let peerId = strongSelf.ssrcMapping[ssrc] { + result.append((peerId, level)) + } + } + if !result.isEmpty { + strongSelf.audioLevelsPipe.putNext(result) + } + })) } } @@ -350,13 +400,47 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { case .estabilished: break default: - if case let .estabilished(_, clientParams, ssrcs, ssrcMapping) = internalState { + if case let .estabilished(_, clientParams, _, ssrcs, ssrcMapping) = internalState { self.ssrcMapping = ssrcMapping self.callContext?.setJoinResponse(payload: clientParams, ssrcs: ssrcs) + if let isCurrentlyConnecting = self.isCurrentlyConnecting, isCurrentlyConnecting { + self.startCheckingCallIfNeeded() + } } } } + private func startCheckingCallIfNeeded() { + if self.checkCallDisposable != nil { + return + } + if case let .estabilished(callInfo, _, ssrc, _, _) = self.internalState { + let checkSignal = checkGroupCall(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, ssrc: Int32(bitPattern: ssrc)) + + self.checkCallDisposable = (( + checkSignal + |> castError(Bool.self) + |> delay(4.0, queue: .mainQueue()) + |> mapToSignal { result -> Signal in + if case .success = result { + return .fail(true) + } else { + return .single(true) + } + } + ) + |> restartIfError + |> take(1) + |> deliverOnMainQueue).start(completed: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.checkCallDisposable = nil + strongSelf.requestCall() + }) + } + } + private func updateIsAudioSessionActive(_ value: Bool) { if self.isAudioSessionActive != value { self.isAudioSessionActive = value @@ -364,7 +448,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } public func leave() -> Signal { - if case let .estabilished(callInfo, _, _, _) = self.internalState { + if case let .estabilished(callInfo, _, _, _, _) = self.internalState { self.leaveDisposable.set((leaveGroupCall(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash) |> deliverOnMainQueue).start(completed: { [weak self] in self?._canBeRemoved.set(.single(true)) @@ -402,7 +486,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } private func requestCall() { - self.internalState = .requesting + self.callContext?.stop() + self.callContext = nil + + self.updateSessionState(internalState: .requesting, audioSessionControl: self.audioSessionControl) enum CallError { case generic diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index fc98673e34..c625b1e63f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -127,7 +127,28 @@ public final class VoiceChatController: ViewController { } private final class Interaction { + private var audioLevels: [PeerId: ValuePipe] = [:] + init() { + } + + func getAudioLevel(_ peerId: PeerId) -> Signal? { + if let current = self.audioLevels[peerId] { + return current.signal() + } else { + let value = ValuePipe() + self.audioLevels[peerId] = value + return value.signal() + } + } + + func updateAudioLevels(levels: [(PeerId, Float)]) { + for (peerId, level) in levels { + if let pipe = self.audioLevels[peerId] { + pipe.putNext(level) + } + } + } } private struct PeerEntry: Comparable, Identifiable { @@ -173,7 +194,7 @@ public final class VoiceChatController: ViewController { //arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) }) }, removePeer: { id in //arguments.deleteIncludePeer(id) - }, noInsets: true) + }, noInsets: true, audioLevel: peer.id == context.account.peerId ? nil : interaction.getAudioLevel(peer.id)) } } @@ -225,6 +246,7 @@ public final class VoiceChatController: ViewController { private var audioOutputStateDisposable: Disposable? private var audioOutputState: ([AudioSessionOutput], AudioSessionOutput?)? + private var audioLevelsDisposable: Disposable? private var memberStatesDisposable: Disposable? private var itemInteraction: Interaction? @@ -347,12 +369,21 @@ public final class VoiceChatController: ViewController { self.audioOutputStateDisposable = (call.audioOutputState |> deliverOnMainQueue).start(next: { [weak self] state in - if let strongSelf = self { - strongSelf.audioOutputState = state - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate) - } + guard let strongSelf = self else { + return } + strongSelf.audioOutputState = state + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate) + } + }) + + self.audioLevelsDisposable = (call.audioLevels + |> deliverOnMainQueue).start(next: { [weak self] levels in + guard let strongSelf = self else { + return + } + strongSelf.itemInteraction?.updateAudioLevels(levels: levels) }) self.leaveNode.addTarget(self, action: #selector(self.leavePressed), forControlEvents: .touchUpInside) @@ -370,6 +401,7 @@ public final class VoiceChatController: ViewController { self.callStateDisposable?.dispose() self.audioOutputStateDisposable?.dispose() self.memberStatesDisposable?.dispose() + self.audioLevelsDisposable?.dispose() } @objc private func leavePressed() { diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index f8a150310c..f8ff085d34 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -137,6 +137,44 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal Signal { + return account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), maxDate: maxDate, limit: limit)) + |> mapError { _ -> GetGroupCallParticipantsError in + return .generic + } + |> map { result -> GetGroupCallParticipantsResult in + var ssrcMapping: [UInt32: PeerId] = [:] + + switch result { + case let .groupParticipants(count, participants, users): + for participant in participants { + var peerId: PeerId? + var ssrc: UInt32? + switch participant { + case let .groupCallParticipant(flags, userId, date, source): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + ssrc = UInt32(bitPattern: source) + } + if let peerId = peerId, let ssrc = ssrc { + ssrcMapping[ssrc] = peerId + } + } + } + + return GetGroupCallParticipantsResult( + ssrcMapping: ssrcMapping + ) + } +} + public enum JoinGroupCallError { case generic } @@ -153,11 +191,17 @@ public func joinGroupCall(account: Account, callId: Int64, accessHash: Int64, jo return .generic } |> mapToSignal { updates -> Signal in - return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash))) - |> mapError { _ -> JoinGroupCallError in - return .generic - } - |> mapToSignal { result -> Signal in + return combineLatest( + account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash))) + |> mapError { _ -> JoinGroupCallError in + return .generic + }, + getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, maxDate: 0, limit: 100) + |> mapError { _ -> JoinGroupCallError in + return .generic + } + ) + |> mapToSignal { result, participantsResult -> Signal in account.stateManager.addUpdates(updates) var maybeParsedCall: GroupCallInfo? @@ -180,7 +224,7 @@ public func joinGroupCall(account: Account, callId: Int64, accessHash: Int64, jo guard let _ = GroupCallInfo(call) else { return .fail(.generic) } - var ssrcMapping: [UInt32: PeerId] = [:] + var ssrcMapping: [UInt32: PeerId] = participantsResult.ssrcMapping for participant in participants { var peerId: PeerId? var ssrc: UInt32? @@ -233,3 +277,23 @@ public func stopGroupCall(account: Account, callId: Int64, accessHash: Int64) -> return .complete() } } + +public enum CheckGroupCallResult { + case success + case restart +} + +public func checkGroupCall(account: Account, callId: Int64, accessHash: Int64, ssrc: Int32) -> Signal { + return account.network.request(Api.functions.phone.checkGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash), source: ssrc)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> map { result -> CheckGroupCallResult in + switch result { + case .boolTrue: + return .success + case .boolFalse: + return .restart + } + } +} diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index 698fe5d4f8..7ca8b5af7b 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -188,7 +188,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { } switch action { - case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall: + case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionInviteToGroupCall: break case let .messageActionChannelMigrateFrom(_, chatId): result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)) diff --git a/submodules/TelegramCore/Sources/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/TelegramMediaAction.swift index eeb04af985..e0ee533e24 100644 --- a/submodules/TelegramCore/Sources/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/TelegramMediaAction.swift @@ -64,6 +64,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe case let .inputGroupCall(id, accessHash): return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, duration: duration)) } + case .messageActionInviteToGroupCall: + return nil } } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 0cbe694529..f3b815faa3 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -210,6 +210,7 @@ swift_library( "//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode", "//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode", "//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode", + "//submodules/AudioBlob:AudioBlob", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d4ca7f16d1..43c568ea24 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5979,11 +5979,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(items), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) }, joinGroupCall: { [weak self] activeCall in - }, editMessageMedia: { [weak self] messageId, draw in - if let strongSelf = self { - strongSelf.controllerInteraction?.editMessageMedia(messageId, draw) - } - }, joinGroupCall: { [weak self] messageId in guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return } @@ -6011,6 +6006,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } } + }, editMessageMedia: { [weak self] messageId, draw in + if let strongSelf = self { + strongSelf.controllerInteraction?.editMessageMedia(messageId, draw) + } }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get())) do { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index e894620c02..e51a148317 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -16,6 +16,7 @@ import SemanticStatusNode import FileMediaResourceStatus import CheckNode import MusicAlbumArtResources +import AudioBlob private struct FetchControls { let fetch: () -> Void diff --git a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift index fbe9b3c273..72d8e1340a 100644 --- a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift @@ -207,7 +207,7 @@ final class ChatPanelInterfaceInteraction { scrollToTop: @escaping () -> Void, viewReplies: @escaping (MessageId?, ChatReplyThreadMessage) -> Void, activatePinnedListPreview: @escaping (ASDisplayNode, ContextGesture) -> Void, - joinGroupCall: @escaping (MessageId) -> Void, + joinGroupCall: @escaping (CachedChannelData.ActiveCall) -> Void, editMessageMedia: @escaping (MessageId, Bool) -> Void, statuses: ChatPanelInterfaceInteractionStatuses? ) { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index 3d77d119be..9398c9f4e9 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -132,8 +132,8 @@ final class ChatRecentActionsController: TelegramBaseController { }, scrollToTop: { }, viewReplies: { _, _ in }, activatePinnedListPreview: { _, _ in - }, editMessageMedia: { _, _ in }, joinGroupCall: { _ in + }, editMessageMedia: { _, _ in }, statuses: nil) self.navigationItem.titleView = self.titleView diff --git a/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift index 24283d889e..d476cb3509 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import LegacyComponents import AccountContext import ChatInterfaceState +import AudioBlob private let offsetThreshold: CGFloat = 10.0 private let dismissOffsetThreshold: CGFloat = 70.0 diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 9ada296a69..0edced3fa0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -438,8 +438,8 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, scrollToTop: { }, viewReplies: { _, _ in }, activatePinnedListPreview: { _, _ in - }, editMessageMedia: { _, _ in }, joinGroupCall: { _ in + }, editMessageMedia: { _, _ in }, statuses: nil) self.selectionPanel.interfaceInteraction = interfaceInteraction diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index f0c4336f96..72080355db 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -44,7 +44,7 @@ public final class OngoingGroupCallContext { var mainStreamAudioSsrc: UInt32? var otherSsrcs: [UInt32] = [] - let joinPayload = Promise() + let joinPayload = Promise<(String, UInt32)>() let networkState = ValuePromise(.connecting, ignoreRepeated: true) let isMuted = ValuePromise(true, ignoreRepeated: true) let memberStates = ValuePromise<[UInt32: MemberState]>([:], ignoreRepeated: true) @@ -105,7 +105,7 @@ public final class OngoingGroupCallContext { return } strongSelf.mainStreamAudioSsrc = ssrc - strongSelf.joinPayload.set(.single(payload)) + strongSelf.joinPayload.set(.single((payload, ssrc))) } }) } @@ -170,6 +170,10 @@ public final class OngoingGroupCallContext { } } + func stop() { + self.context.stop() + } + func setIsMuted(_ isMuted: Bool) { self.isMuted.set(isMuted) self.context.setIsMuted(isMuted) @@ -179,7 +183,7 @@ public final class OngoingGroupCallContext { private let queue = Queue() private let impl: QueueLocalObject - public var joinPayload: Signal { + public var joinPayload: Signal<(String, UInt32), NoError> { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in @@ -269,4 +273,10 @@ public final class OngoingGroupCallContext { impl.removeSsrcs(ssrcs: ssrcs) } } + + public func stop() { + self.impl.with { impl in + impl.stop() + } + } } diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h index f48bd09e35..63d2ded655 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h @@ -160,6 +160,8 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) { - (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated; +- (void)stop; + - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion; - (void)setJoinResponsePayload:(NSString * _Nonnull)payload; - (void)setSsrcs:(NSArray * _Nonnull)ssrcs; diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 0e88cb7882..a38f9e4ed3 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -826,18 +826,26 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; networkStateUpdated(isConnected ? GroupCallNetworkStateConnected : GroupCallNetworkStateConnecting); }]; }, - .audioLevelsUpdated = [weakSelf, queue, audioLevelsUpdated](std::vector> const &levels) { + .audioLevelsUpdated = [audioLevelsUpdated](std::vector> const &levels) { NSMutableArray *result = [[NSMutableArray alloc] init]; for (auto &it : levels) { [result addObject:@(it.first)]; [result addObject:@(it.second)]; } + audioLevelsUpdated(result); } })); } return self; } +- (void)stop { + if (_instance) { + _instance->stop(); + _instance.reset(); + } +} + - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion { if (_instance) { _instance->emitJoinPayload([completion](tgcalls::GroupJoinPayload payload) { From 8e8e2d580964df5d3c1c1109be027a1b3e583694 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Nov 2020 22:35:46 +0400 Subject: [PATCH 2/6] Fix build --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index f679bba273..c0a1340e81 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit f679bba27327cfe0327a89816d4ba56355b9c9c7 +Subproject commit c0a1340e81d4c4ee27307164afc2f43b4183851a From eaeb7c266318e58e83456741522bc1adf359fd1a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Nov 2020 22:54:21 +0400 Subject: [PATCH 3/6] Save more artifacts --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index acbb37f19a..604a646f78 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,7 @@ internal: name: internal artifacts: paths: - - build/artifacts/Telegram.DSYMs.zip + - build/artifacts expire_in: 1 week experimental: From 1e471d8c9959150b1cc3d1508c48dde742bed77b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Nov 2020 23:00:27 +0400 Subject: [PATCH 4/6] Update ci --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 604a646f78..c171ae2161 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,6 +23,7 @@ internal: environment: name: internal artifacts: + when: on_failure paths: - build/artifacts expire_in: 1 week From c64a28f022c12d29bf0dcbbe7a0b2f7d65968310 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Nov 2020 23:20:48 +0400 Subject: [PATCH 5/6] Update API --- submodules/TelegramApi/Sources/Api0.swift | 6 +-- submodules/TelegramApi/Sources/Api1.swift | 42 +++++++++---------- submodules/TelegramApi/Sources/Api3.swift | 35 ++++++---------- .../TelegramCore/Sources/GroupCalls.swift | 10 ++--- 4 files changed, 39 insertions(+), 54 deletions(-) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index dc4989ab65..3e2cffef91 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -7,7 +7,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1255641564] = { return parseString($0) } dict[-1240849242] = { return Api.messages.StickerSet.parse_stickerSet($0) } dict[1829443076] = { return Api.GroupCall.parse_groupCallPrivate($0) } - dict[1441699306] = { return Api.GroupCall.parse_groupCall($0) } + dict[2083222527] = { return Api.GroupCall.parse_groupCall($0) } dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) } dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) } dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } @@ -503,7 +503,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1495959709] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) } dict[411017418] = { return Api.SecureValue.parse_secureValue($0) } dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) } - dict[1118525718] = { return Api.phone.GroupCall.parse_groupCall($0) } + dict[1447862232] = { return Api.phone.GroupCall.parse_groupCall($0) } dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) } dict[-2128698738] = { return Api.auth.CheckedPhone.parse_checkedPhone($0) } dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) } @@ -881,7 +881,7 @@ public struct Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") return nil } } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 5a3022bcde..b228a213c0 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -1911,7 +1911,7 @@ public struct messages { public extension Api { public enum GroupCall: TypeConstructorDescription { case groupCallPrivate(flags: Int32, id: Int64, accessHash: Int64, channelId: Int32?, participantsCount: Int32, adminId: Int32) - case groupCall(flags: Int32, id: Int64, accessHash: Int64, channelId: Int32?, adminId: Int32, reflectorId: Int64, params: Api.DataJSON?, version: Int32) + case groupCall(flags: Int32, id: Int64, accessHash: Int64, adminId: Int32, reflectorId: Int64, params: Api.DataJSON?, version: Int32) case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { @@ -1927,17 +1927,16 @@ public extension Api { serializeInt32(participantsCount, buffer: buffer, boxed: false) serializeInt32(adminId, buffer: buffer, boxed: false) break - case .groupCall(let flags, let id, let accessHash, let channelId, let adminId, let reflectorId, let params, let version): + case .groupCall(let flags, let id, let accessHash, let adminId, let reflectorId, let params, let version): if boxed { - buffer.appendInt32(1441699306) + buffer.appendInt32(2083222527) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(accessHash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(channelId!, buffer: buffer, boxed: false)} serializeInt32(adminId, buffer: buffer, boxed: false) serializeInt64(reflectorId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {params!.serialize(buffer, true)} + if Int(flags) & Int(1 << 0) != 0 {params!.serialize(buffer, true)} serializeInt32(version, buffer: buffer, boxed: false) break case .groupCallDiscarded(let id, let accessHash, let duration): @@ -1955,8 +1954,8 @@ public extension Api { switch self { case .groupCallPrivate(let flags, let id, let accessHash, let channelId, let participantsCount, let adminId): return ("groupCallPrivate", [("flags", flags), ("id", id), ("accessHash", accessHash), ("channelId", channelId), ("participantsCount", participantsCount), ("adminId", adminId)]) - case .groupCall(let flags, let id, let accessHash, let channelId, let adminId, let reflectorId, let params, let version): - return ("groupCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("channelId", channelId), ("adminId", adminId), ("reflectorId", reflectorId), ("params", params), ("version", version)]) + case .groupCall(let flags, let id, let accessHash, let adminId, let reflectorId, let params, let version): + return ("groupCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("adminId", adminId), ("reflectorId", reflectorId), ("params", params), ("version", version)]) case .groupCallDiscarded(let id, let accessHash, let duration): return ("groupCallDiscarded", [("id", id), ("accessHash", accessHash), ("duration", duration)]) } @@ -1996,27 +1995,24 @@ public extension Api { var _3: Int64? _3 = reader.readInt64() var _4: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } - var _5: Int32? - _5 = reader.readInt32() - var _6: Int64? - _6 = reader.readInt64() - var _7: Api.DataJSON? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.DataJSON + _4 = reader.readInt32() + var _5: Int64? + _5 = reader.readInt64() + var _6: Api.DataJSON? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.DataJSON } } - var _8: Int32? - _8 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c4 = _4 != nil let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, channelId: _4, adminId: _5!, reflectorId: _6!, params: _7, version: _8!) + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, adminId: _4!, reflectorId: _5!, params: _6, version: _7!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index c096955a8a..44f25f5feb 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -1649,13 +1649,13 @@ public struct photos { public extension Api { public struct phone { public enum GroupCall: TypeConstructorDescription { - case groupCall(call: Api.GroupCall, sources: [Int32], participants: [Api.GroupCallParticipant], chats: [Api.Chat], users: [Api.User]) + case groupCall(call: Api.GroupCall, sources: [Int32], participants: [Api.GroupCallParticipant], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .groupCall(let call, let sources, let participants, let chats, let users): + case .groupCall(let call, let sources, let participants, let users): if boxed { - buffer.appendInt32(1118525718) + buffer.appendInt32(1447862232) } call.serialize(buffer, true) buffer.appendInt32(481674261) @@ -1669,11 +1669,6 @@ public struct phone { item.serialize(buffer, true) } buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { item.serialize(buffer, true) @@ -1684,8 +1679,8 @@ public struct phone { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .groupCall(let call, let sources, let participants, let chats, let users): - return ("groupCall", [("call", call), ("sources", sources), ("participants", participants), ("chats", chats), ("users", users)]) + case .groupCall(let call, let sources, let participants, let users): + return ("groupCall", [("call", call), ("sources", sources), ("participants", participants), ("users", users)]) } } @@ -1702,21 +1697,16 @@ public struct phone { if let _ = reader.readInt32() { _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) } - var _4: [Api.Chat]? + var _4: [Api.User]? if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.phone.GroupCall.groupCall(call: _1!, sources: _2!, participants: _3!, chats: _4!, users: _5!) + if _c1 && _c2 && _c3 && _c4 { + return Api.phone.GroupCall.groupCall(call: _1!, sources: _2!, participants: _3!, users: _4!) } else { return nil @@ -7249,13 +7239,12 @@ public extension Api { }) } - public static func createGroupCall(flags: Int32, channel: Api.InputChannel, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func createGroupCall(channel: Api.InputChannel, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1542553507) - serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(-467076606) channel.serialize(buffer, true) serializeInt32(randomId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", flags), ("channel", channel), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + return (FunctionDescription(name: "phone.createGroupCall", parameters: [("channel", channel), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index f8ff085d34..c59fdd5fa8 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -23,7 +23,7 @@ private extension GroupCallInfo { clientParams: nil, version: nil ) - case let .groupCall(_, id, accessHash, channelId, _, _, params, version): + case let .groupCall(_, id, accessHash, _, _, params, version): var clientParams: String? if let params = params { switch params { @@ -34,7 +34,7 @@ private extension GroupCallInfo { self.init( id: id, accessHash: accessHash, - peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: $0) }, + peerId: nil, clientParams: clientParams, version: version ) @@ -84,7 +84,7 @@ public func getCurrentGroupCall(account: Account, peerId: PeerId) -> Signal mapToSignal { result -> Signal in switch result { - case let .groupCall(call, sources, participants, chats, users): + case let .groupCall(call, sources, participants, users): return account.postbox.transaction { transaction -> GroupCallInfo? in return GroupCallInfo(call) } @@ -110,7 +110,7 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal mapError { _ -> CreateGroupCallError in return .generic } @@ -220,7 +220,7 @@ public func joinGroupCall(account: Account, callId: Int64, accessHash: Int64, jo } switch result { - case let .groupCall(call, sources, participants, chats, users): + case let .groupCall(call, sources, participants, users): guard let _ = GroupCallInfo(call) else { return .fail(.generic) } From b2f3ed149189a662a468fc00b0a2c40fe7e5f2a5 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 21 Nov 2020 00:57:16 +0400 Subject: [PATCH 6/6] Fix audio session state --- .../TelegramCallsUI/Sources/PresentationGroupCall.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 06583f0b6c..e5f2d4e78b 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -226,8 +226,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } }) - self.requestCall() - self.groupCallParticipantUpdatesDisposable = (self.account.stateManager.groupCallParticipantUpdates |> deliverOnMainQueue).start(next: { [weak self] updates in guard let strongSelf = self else { @@ -255,6 +253,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } }) + + self.requestCall() } deinit { @@ -489,7 +489,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.callContext?.stop() self.callContext = nil - self.updateSessionState(internalState: .requesting, audioSessionControl: self.audioSessionControl) + self.internalState = .requesting + self.isCurrentlyConnecting = nil enum CallError { case generic