[WIP] Conference calls

This commit is contained in:
Isaac 2024-12-18 23:35:27 +08:00
parent e18795980e
commit 2b9f5ed541
22 changed files with 619 additions and 407 deletions

View File

@ -283,6 +283,9 @@
"LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages"; "LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages";
"LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages"; "LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages";
"PUSH_STORY_HIDDEN_AUTHOR" = "New story was posted";
"PUSH_MESSAGE_STARGIFT" = "%1$@|sent you a Gift worth of %2$@ stars";
// Common // Common
"Common.OK" = "OK"; "Common.OK" = "OK";
"Common.Cancel" = "Cancel"; "Common.Cancel" = "Cancel";

View File

@ -535,7 +535,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) } dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) }
dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
dict[-1808510398] = { return Api.Message.parse_message($0) } dict[-1761756183] = { return Api.Message.parse_message($0) }
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
dict[-741178048] = { return Api.Message.parse_messageService($0) } dict[-741178048] = { return Api.Message.parse_messageService($0) }
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) } dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
@ -723,11 +723,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1395233698] = { return Api.PeerSettings.parse_peerSettings($0) } dict[-1395233698] = { return Api.PeerSettings.parse_peerSettings($0) }
dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) } dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) }
dict[1000707084] = { return Api.PhoneCall.parse_phoneCall($0) } dict[1000707084] = { return Api.PhoneCall.parse_phoneCall($0) }
dict[912311057] = { return Api.PhoneCall.parse_phoneCallAccepted($0) } dict[587035009] = { return Api.PhoneCall.parse_phoneCallAccepted($0) }
dict[1355435489] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) } dict[-103656189] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) }
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) } dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
dict[347139340] = { return Api.PhoneCall.parse_phoneCallRequested($0) } dict[1161174115] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
dict[-987599081] = { return Api.PhoneCall.parse_phoneCallWaiting($0) } dict[-288085928] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
dict[-1344096199] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonAllowGroupCall($0) }
dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) } dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) }
dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) } dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) }
dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) } dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) }

View File

@ -60,15 +60,15 @@ public extension Api {
} }
public extension Api { public extension Api {
indirect enum Message: TypeConstructorDescription { indirect enum Message: TypeConstructorDescription {
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?) case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?)
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?) case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?) case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck): case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate):
if boxed { if boxed {
buffer.appendInt32(-1808510398) buffer.appendInt32(-1761756183)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(flags2, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false)
@ -106,6 +106,7 @@ public extension Api {
if Int(flags) & Int(1 << 30) != 0 {serializeInt32(quickReplyShortcutId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 30) != 0 {serializeInt32(quickReplyShortcutId!, buffer: buffer, boxed: false)}
if Int(flags2) & Int(1 << 2) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 2) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)}
if Int(flags2) & Int(1 << 3) != 0 {factcheck!.serialize(buffer, true)} if Int(flags2) & Int(1 << 3) != 0 {factcheck!.serialize(buffer, true)}
if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(reportDeliveryUntilDate!, buffer: buffer, boxed: false)}
break break
case .messageEmpty(let flags, let id, let peerId): case .messageEmpty(let flags, let id, let peerId):
if boxed { if boxed {
@ -134,8 +135,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck): case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate):
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any)]) return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any)])
case .messageEmpty(let flags, let id, let peerId): case .messageEmpty(let flags, let id, let peerId):
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)]) return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let reactions, let ttlPeriod): case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let reactions, let ttlPeriod):
@ -224,6 +225,8 @@ public extension Api {
if Int(_2!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { if Int(_2!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_28 = Api.parse(reader, signature: signature) as? Api.FactCheck _28 = Api.parse(reader, signature: signature) as? Api.FactCheck
} } } }
var _29: Int32?
if Int(_2!) & Int(1 << 5) != 0 {_29 = reader.readInt32() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -252,8 +255,9 @@ public extension Api {
let _c26 = (Int(_1!) & Int(1 << 30) == 0) || _26 != nil let _c26 = (Int(_1!) & Int(1 << 30) == 0) || _26 != nil
let _c27 = (Int(_2!) & Int(1 << 2) == 0) || _27 != nil let _c27 = (Int(_2!) & Int(1 << 2) == 0) || _27 != nil
let _c28 = (Int(_2!) & Int(1 << 3) == 0) || _28 != nil let _c28 = (Int(_2!) & Int(1 << 3) == 0) || _28 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 { let _c29 = (Int(_2!) & Int(1 << 5) == 0) || _29 != nil
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 {
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28, reportDeliveryUntilDate: _29)
} }
else { else {
return nil return nil

View File

@ -1011,11 +1011,11 @@ public extension Api {
public extension Api { public extension Api {
enum PhoneCall: TypeConstructorDescription { enum PhoneCall: TypeConstructorDescription {
case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?, conferenceCall: Api.InputGroupCall?) case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?, conferenceCall: Api.InputGroupCall?)
case phoneCallAccepted(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gB: Buffer, protocol: Api.PhoneCallProtocol) case phoneCallAccepted(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gB: Buffer, protocol: Api.PhoneCallProtocol, conferenceCall: Api.InputGroupCall?)
case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?) case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?, conferenceCall: Api.InputGroupCall?)
case phoneCallEmpty(id: Int64) case phoneCallEmpty(id: Int64)
case phoneCallRequested(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAHash: Buffer, protocol: Api.PhoneCallProtocol) case phoneCallRequested(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAHash: Buffer, protocol: Api.PhoneCallProtocol, conferenceCall: Api.InputGroupCall?)
case phoneCallWaiting(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, protocol: Api.PhoneCallProtocol, receiveDate: Int32?) case phoneCallWaiting(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, protocol: Api.PhoneCallProtocol, receiveDate: Int32?, conferenceCall: Api.InputGroupCall?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -1041,9 +1041,9 @@ public extension Api {
if Int(flags) & Int(1 << 7) != 0 {customParameters!.serialize(buffer, true)} if Int(flags) & Int(1 << 7) != 0 {customParameters!.serialize(buffer, true)}
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)} if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
break break
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`): case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`, let conferenceCall):
if boxed { if boxed {
buffer.appendInt32(912311057) buffer.appendInt32(587035009)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false)
@ -1053,15 +1053,17 @@ public extension Api {
serializeInt64(participantId, buffer: buffer, boxed: false) serializeInt64(participantId, buffer: buffer, boxed: false)
serializeBytes(gB, buffer: buffer, boxed: false) serializeBytes(gB, buffer: buffer, boxed: false)
`protocol`.serialize(buffer, true) `protocol`.serialize(buffer, true)
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
break break
case .phoneCallDiscarded(let flags, let id, let reason, let duration): case .phoneCallDiscarded(let flags, let id, let reason, let duration, let conferenceCall):
if boxed { if boxed {
buffer.appendInt32(1355435489) buffer.appendInt32(-103656189)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
break break
case .phoneCallEmpty(let id): case .phoneCallEmpty(let id):
if boxed { if boxed {
@ -1069,9 +1071,9 @@ public extension Api {
} }
serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false)
break break
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`): case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`, let conferenceCall):
if boxed { if boxed {
buffer.appendInt32(347139340) buffer.appendInt32(1161174115)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false)
@ -1081,10 +1083,11 @@ public extension Api {
serializeInt64(participantId, buffer: buffer, boxed: false) serializeInt64(participantId, buffer: buffer, boxed: false)
serializeBytes(gAHash, buffer: buffer, boxed: false) serializeBytes(gAHash, buffer: buffer, boxed: false)
`protocol`.serialize(buffer, true) `protocol`.serialize(buffer, true)
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
break break
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate): case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate, let conferenceCall):
if boxed { if boxed {
buffer.appendInt32(-987599081) buffer.appendInt32(-288085928)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false)
@ -1094,6 +1097,7 @@ public extension Api {
serializeInt64(participantId, buffer: buffer, boxed: false) serializeInt64(participantId, buffer: buffer, boxed: false)
`protocol`.serialize(buffer, true) `protocol`.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(receiveDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(receiveDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
break break
} }
} }
@ -1102,16 +1106,16 @@ public extension Api {
switch self { switch self {
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall): case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall):
return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any), ("conferenceCall", conferenceCall as Any)]) return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any), ("conferenceCall", conferenceCall as Any)])
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`): case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`, let conferenceCall):
return ("phoneCallAccepted", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gB", gB as Any), ("`protocol`", `protocol` as Any)]) return ("phoneCallAccepted", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gB", gB as Any), ("`protocol`", `protocol` as Any), ("conferenceCall", conferenceCall as Any)])
case .phoneCallDiscarded(let flags, let id, let reason, let duration): case .phoneCallDiscarded(let flags, let id, let reason, let duration, let conferenceCall):
return ("phoneCallDiscarded", [("flags", flags as Any), ("id", id as Any), ("reason", reason as Any), ("duration", duration as Any)]) return ("phoneCallDiscarded", [("flags", flags as Any), ("id", id as Any), ("reason", reason as Any), ("duration", duration as Any), ("conferenceCall", conferenceCall as Any)])
case .phoneCallEmpty(let id): case .phoneCallEmpty(let id):
return ("phoneCallEmpty", [("id", id as Any)]) return ("phoneCallEmpty", [("id", id as Any)])
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`): case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`, let conferenceCall):
return ("phoneCallRequested", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAHash", gAHash as Any), ("`protocol`", `protocol` as Any)]) return ("phoneCallRequested", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAHash", gAHash as Any), ("`protocol`", `protocol` as Any), ("conferenceCall", conferenceCall as Any)])
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate): case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate, let conferenceCall):
return ("phoneCallWaiting", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("`protocol`", `protocol` as Any), ("receiveDate", receiveDate as Any)]) return ("phoneCallWaiting", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("`protocol`", `protocol` as Any), ("receiveDate", receiveDate as Any), ("conferenceCall", conferenceCall as Any)])
} }
} }
@ -1189,6 +1193,10 @@ public extension Api {
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol _8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
} }
var _9: Api.InputGroupCall?
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -1197,8 +1205,9 @@ public extension Api {
let _c6 = _6 != nil let _c6 = _6 != nil
let _c7 = _7 != nil let _c7 = _7 != nil
let _c8 = _8 != nil let _c8 = _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!, conferenceCall: _9)
} }
else { else {
return nil return nil
@ -1215,12 +1224,17 @@ public extension Api {
} } } }
var _4: Int32? var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() } if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Api.InputGroupCall?
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 { let _c5 = (Int(_1!) & Int(1 << 8) == 0) || _5 != nil
return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4) if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4, conferenceCall: _5)
} }
else { else {
return nil return nil
@ -1256,6 +1270,10 @@ public extension Api {
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol _8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
} }
var _9: Api.InputGroupCall?
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -1264,8 +1282,9 @@ public extension Api {
let _c6 = _6 != nil let _c6 = _6 != nil
let _c7 = _7 != nil let _c7 = _7 != nil
let _c8 = _8 != nil let _c8 = _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!, conferenceCall: _9)
} }
else { else {
return nil return nil
@ -1290,6 +1309,10 @@ public extension Api {
} }
var _8: Int32? var _8: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt32() } if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt32() }
var _9: Api.InputGroupCall?
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -1298,8 +1321,9 @@ public extension Api {
let _c6 = _6 != nil let _c6 = _6 != nil
let _c7 = _7 != nil let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8, conferenceCall: _9)
} }
else { else {
return nil return nil
@ -1310,6 +1334,7 @@ public extension Api {
} }
public extension Api { public extension Api {
enum PhoneCallDiscardReason: TypeConstructorDescription { enum PhoneCallDiscardReason: TypeConstructorDescription {
case phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer)
case phoneCallDiscardReasonBusy case phoneCallDiscardReasonBusy
case phoneCallDiscardReasonDisconnect case phoneCallDiscardReasonDisconnect
case phoneCallDiscardReasonHangup case phoneCallDiscardReasonHangup
@ -1317,6 +1342,12 @@ public extension Api {
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .phoneCallDiscardReasonAllowGroupCall(let encryptedKey):
if boxed {
buffer.appendInt32(-1344096199)
}
serializeBytes(encryptedKey, buffer: buffer, boxed: false)
break
case .phoneCallDiscardReasonBusy: case .phoneCallDiscardReasonBusy:
if boxed { if boxed {
buffer.appendInt32(-84416311) buffer.appendInt32(-84416311)
@ -1346,6 +1377,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .phoneCallDiscardReasonAllowGroupCall(let encryptedKey):
return ("phoneCallDiscardReasonAllowGroupCall", [("encryptedKey", encryptedKey as Any)])
case .phoneCallDiscardReasonBusy: case .phoneCallDiscardReasonBusy:
return ("phoneCallDiscardReasonBusy", []) return ("phoneCallDiscardReasonBusy", [])
case .phoneCallDiscardReasonDisconnect: case .phoneCallDiscardReasonDisconnect:
@ -1357,6 +1390,17 @@ public extension Api {
} }
} }
public static func parse_phoneCallDiscardReasonAllowGroupCall(_ reader: BufferReader) -> PhoneCallDiscardReason? {
var _1: Buffer?
_1 = parseBytes(reader)
let _c1 = _1 != nil
if _c1 {
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonAllowGroupCall(encryptedKey: _1!)
}
else {
return nil
}
}
public static func parse_phoneCallDiscardReasonBusy(_ reader: BufferReader) -> PhoneCallDiscardReason? { public static func parse_phoneCallDiscardReasonBusy(_ reader: BufferReader) -> PhoneCallDiscardReason? {
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonBusy return Api.PhoneCallDiscardReason.phoneCallDiscardReasonBusy
} }

View File

@ -7483,6 +7483,27 @@ public extension Api.functions.messages {
}) })
} }
} }
public extension Api.functions.messages {
static func reportMessagesDelivery(flags: Int32, peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(1517122453)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(id.count))
for item in id {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "messages.reportMessagesDelivery", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Bool
}
return result
})
}
}
public extension Api.functions.messages { public extension Api.functions.messages {
static func reportReaction(peer: Api.InputPeer, id: Int32, reactionPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) { static func reportReaction(peer: Api.InputPeer, id: Int32, reactionPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer() let buffer = Buffer()
@ -9587,11 +9608,12 @@ public extension Api.functions.phone {
} }
} }
public extension Api.functions.phone { public extension Api.functions.phone {
static func createConferenceCall(peer: Api.InputPhoneCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) { static func createConferenceCall(peer: Api.InputPhoneCall, keyFingerprint: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-1828162221) buffer.appendInt32(-540472917)
peer.serialize(buffer, true) peer.serialize(buffer, true)
return (FunctionDescription(name: "phone.createConferenceCall", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in serializeInt64(keyFingerprint, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.createConferenceCall", parameters: [("peer", String(describing: peer)), ("keyFingerprint", String(describing: keyFingerprint))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.phone.PhoneCall? var result: Api.phone.PhoneCall?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -9834,15 +9856,16 @@ public extension Api.functions.phone {
} }
} }
public extension Api.functions.phone { public extension Api.functions.phone {
static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, keyFingerprint: Int64?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-1322057861) buffer.appendInt32(-702669325)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true) call.serialize(buffer, true)
joinAs.serialize(buffer, true) joinAs.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(keyFingerprint!, buffer: buffer, boxed: false)}
params.serialize(buffer, true) params.serialize(buffer, true)
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("keyFingerprint", String(describing: keyFingerprint)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -9915,15 +9938,16 @@ public extension Api.functions.phone {
} }
} }
public extension Api.functions.phone { public extension Api.functions.phone {
static func requestCall(flags: Int32, userId: Api.InputUser, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) { static func requestCall(flags: Int32, userId: Api.InputUser, conferenceCall: Api.InputGroupCall?, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(1124046573) buffer.appendInt32(-1497079796)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
userId.serialize(buffer, true) userId.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {conferenceCall!.serialize(buffer, true)}
serializeInt32(randomId, buffer: buffer, boxed: false) serializeInt32(randomId, buffer: buffer, boxed: false)
serializeBytes(gAHash, buffer: buffer, boxed: false) serializeBytes(gAHash, buffer: buffer, boxed: false)
`protocol`.serialize(buffer, true) `protocol`.serialize(buffer, true)
return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("conferenceCall", String(describing: conferenceCall)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.phone.PhoneCall? var result: Api.phone.PhoneCall?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -522,6 +522,13 @@ public final class CallController: ViewController {
let _ = self?.dismiss() let _ = self?.dismiss()
} }
displayNode.conferenceAddParticipant = { [weak self] in
guard let self else {
return
}
self.conferenceAddParticipant()
}
self.controllerNode.presentCallRating = { [weak self] callId, isVideo in self.controllerNode.presentCallRating = { [weak self] callId, isVideo in
if let strongSelf = self, !strongSelf.presentedCallRating { if let strongSelf = self, !strongSelf.presentedCallRating {
strongSelf.presentedCallRating = true strongSelf.presentedCallRating = true
@ -667,6 +674,35 @@ public final class CallController: ViewController {
}) })
} }
private func conferenceAddParticipant() {
let controller = self.call.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(
context: self.call.context,
filter: [.onlyWriteable],
hasChatListSelector: true,
hasContactSelector: true,
hasGlobalSearch: true,
title: "Add Participant",
pretendPresentedInModal: false
))
controller.peerSelected = { [weak self, weak controller] peer, _ in
controller?.dismiss()
guard let self else {
return
}
guard case let .call(call) = self.call else {
return
}
guard let call = call as? PresentationCallImpl else {
return
}
let _ = call.requestAddToConference(peerId: peer.id)
}
self.dismiss()
(self.call.context.sharedContext.mainWindow?.viewController as? NavigationController)?.pushViewController(controller)
}
@objc private func backPressed() { @objc private func backPressed() {
self.dismiss() self.dismiss()
} }

View File

@ -60,6 +60,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
var dismissedInteractively: (() -> Void)? var dismissedInteractively: (() -> Void)?
var dismissAllTooltips: (() -> Void)? var dismissAllTooltips: (() -> Void)?
var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)? var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)?
var conferenceAddParticipant: (() -> Void)?
private var emojiKey: (data: Data, resolvedKey: [String])? private var emojiKey: (data: Data, resolvedKey: [String])?
private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat)? private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat)?
@ -129,6 +130,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
guard let self else { guard let self else {
return return
} }
#if DEBUG
if self.sharedContext.immediateExperimentalUISettings.conferenceCalls {
self.conferenceAddParticipant?()
return
}
#endif
self.call.toggleIsMuted() self.call.toggleIsMuted()
} }
self.callScreen.endCallAction = { [weak self] in self.callScreen.endCallAction = { [weak self] in
@ -157,6 +166,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
} }
self.restoreUIForPictureInPicture?(completion) self.restoreUIForPictureInPicture?(completion)
} }
self.callScreen.conferenceAddParticipant = { [weak self] in
guard let self else {
return
}
self.conferenceAddParticipant?()
}
self.callScreenState = PrivateCallScreen.State( self.callScreenState = PrivateCallScreen.State(
strings: presentationData.strings, strings: presentationData.strings,

View File

@ -550,9 +550,9 @@ public final class PresentationCallImpl: PresentationCall {
presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel) presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .terminated(id, reason, options): case let .terminated(id, reason, options):
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel) presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .requesting(ringing): case let .requesting(ringing, _):
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel) presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .active(_, _, keyVisualHash, _, _, _, _, _, _): case let .active(_, _, keyVisualHash, _, _, _, _, _, _), let .switchedToConference(_, keyVisualHash, _):
self.callWasActive = true self.callWasActive = true
if let callContextState = callContextState { if let callContextState = callContextState {
switch callContextState.state { switch callContextState.state {
@ -585,13 +585,22 @@ public final class PresentationCallImpl: PresentationCall {
} }
} }
var conferenceCallData: (key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)?
var conferenceFromCallId: CallId?
switch sessionState.state { switch sessionState.state {
case .requesting: case let .active(id, key, keyVisualHash, _, _, _, _, _, conferenceCall):
if let _ = audioSessionControl { if let conferenceCall {
self.audioSessionShouldBeActive.set(true) conferenceFromCallId = id
conferenceCallData = (key, keyVisualHash, conferenceCall)
} }
case let .active(id, key, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall): case let .switchedToConference(key, keyVisualHash, conferenceCall):
if let conferenceCall, self.conferenceCallDisposable == nil { conferenceCallData = (key, keyVisualHash, conferenceCall)
default:
break
}
if let (key, keyVisualHash, conferenceCall) = conferenceCallData {
if self.conferenceCallDisposable == nil {
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel) presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
self.conferenceCallDisposable = (self.context.engine.calls.getCurrentGroupCall(callId: conferenceCall.id, accessHash: conferenceCall.accessHash) self.conferenceCallDisposable = (self.context.engine.calls.getCurrentGroupCall(callId: conferenceCall.id, accessHash: conferenceCall.accessHash)
@ -620,8 +629,8 @@ public final class PresentationCallImpl: PresentationCall {
invite: nil, invite: nil,
joinAsPeerId: nil, joinAsPeerId: nil,
isStream: false, isStream: false,
encryptionKey: key, encryptionKey: (key, 1),
conferenceFromCallId: id, conferenceFromCallId: conferenceFromCallId,
isConference: true, isConference: true,
sharedAudioDevice: self.sharedAudioDevice sharedAudioDevice: self.sharedAudioDevice
) )
@ -659,7 +668,8 @@ public final class PresentationCallImpl: PresentationCall {
conferenceCall.hasActiveIncomingData conferenceCall.hasActiveIncomingData
) )
|> map { remoteConferenceIsConnected, hasActiveIncomingData -> Bool in |> map { remoteConferenceIsConnected, hasActiveIncomingData -> Bool in
return remoteConferenceIsConnected || hasActiveIncomingData //return remoteConferenceIsConnected || hasActiveIncomingData
return true
} }
|> distinctUntilChanged |> distinctUntilChanged
@ -787,14 +797,21 @@ public final class PresentationCallImpl: PresentationCall {
}) })
}) })
} }
}
switch sessionState.state {
case .requesting:
if let _ = audioSessionControl {
self.audioSessionShouldBeActive.set(true)
}
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
if conferenceCall == nil, self.isExpectedToBeConference { if conferenceCall == nil, self.isExpectedToBeConference {
self.createConferenceIfPossible() self.createConferenceIfPossible()
} }
self.audioSessionShouldBeActive.set(true) self.audioSessionShouldBeActive.set(true)
if self.isExpectedToBeConference { if self.isExpectedToBeConference || conferenceCallData != nil {
if sessionState.isOutgoing { if sessionState.isOutgoing {
self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date()) self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date())
} }
@ -861,6 +878,8 @@ public final class PresentationCallImpl: PresentationCall {
}) })
} }
} }
case .switchedToConference:
self.audioSessionShouldBeActive.set(true)
case let .terminated(_, _, options): case let .terminated(_, _, options):
self.audioSessionShouldBeActive.set(true) self.audioSessionShouldBeActive.set(true)
if wasActive { if wasActive {
@ -1093,9 +1112,9 @@ public final class PresentationCallImpl: PresentationCall {
} }
private func sendConferenceSignalingMessage(dict: [String: Any]) { private func sendConferenceSignalingMessage(dict: [String: Any]) {
if let data = try? JSONSerialization.data(withJSONObject: dict) { /*if let data = try? JSONSerialization.data(withJSONObject: dict) {
self.context.account.callSessionManager.sendSignalingData(internalId: self.internalId, data: data) self.context.account.callSessionManager.sendSignalingData(internalId: self.internalId, data: data)
} }*/
} }
private func updateIsAudioSessionActive(_ value: Bool) { private func updateIsAudioSessionActive(_ value: Bool) {
@ -1272,6 +1291,32 @@ public final class PresentationCallImpl: PresentationCall {
self.videoCapturer?.setIsVideoEnabled(!isPaused) self.videoCapturer?.setIsVideoEnabled(!isPaused)
} }
public func requestAddToConference(peerId: EnginePeer.Id) -> Disposable {
var conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?
if let sessionState = self.sessionState {
switch sessionState.state {
case let .active(_, key, _, _, _, _, _, _, conferenceCallValue):
if let conferenceCallValue {
conferenceCall = (conferenceCallValue, key)
}
case let .switchedToConference(key, _, conferenceCallValue):
conferenceCall = (conferenceCallValue, key)
default:
break
}
}
guard let conferenceCall else {
return EmptyDisposable
}
return (self.callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: conferenceCall)
|> deliverOnMainQueue).startStandalone(next: { [weak self] requestedInternalId in
guard let self else {
return
}
let _ = self
})
}
public func setCurrentAudioOutput(_ output: AudioSessionOutput) { public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
guard self.currentAudioOutputValue != output else { guard self.currentAudioOutputValue != output else {
return return

View File

@ -536,7 +536,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|> mapToSignal { areVideoCallsAvailable -> Signal<CallSessionInternalId, NoError> in |> mapToSignal { areVideoCallsAvailable -> Signal<CallSessionInternalId, NoError> in
let isVideoPossible: Bool = areVideoCallsAvailable let isVideoPossible: Bool = areVideoCallsAvailable
return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId) return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, conferenceCall: nil, internalId: internalId)
} }
return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), context.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

View File

@ -865,7 +865,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var screencastStateDisposable: Disposable? private var screencastStateDisposable: Disposable?
public let isStream: Bool public let isStream: Bool
private let encryptionKey: Data? private let encryptionKey: (key: Data, fingerprint: Int64)?
private let sharedAudioDevice: OngoingCallContext.AudioDevice? private let sharedAudioDevice: OngoingCallContext.AudioDevice?
private let conferenceFromCallId: CallId? private let conferenceFromCallId: CallId?
@ -885,7 +885,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
invite: String?, invite: String?,
joinAsPeerId: EnginePeer.Id?, joinAsPeerId: EnginePeer.Id?,
isStream: Bool, isStream: Bool,
encryptionKey: Data?, encryptionKey: (key: Data, fingerprint: Int64)?,
conferenceFromCallId: CallId?, conferenceFromCallId: CallId?,
isConference: Bool, isConference: Bool,
sharedAudioDevice: OngoingCallContext.AudioDevice? sharedAudioDevice: OngoingCallContext.AudioDevice?
@ -1711,16 +1711,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
var encryptionKey: Data? var encryptionKey: Data?
encryptionKey = self.encryptionKey encryptionKey = self.encryptionKey?.key
#if DEBUG
if encryptionKey == nil {
encryptionKey = Data(count: 256)
}
if "".isEmpty { if "".isEmpty {
encryptionKey = nil encryptionKey = nil
} }
#endif
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: self.audioSessionActive.get(), video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: self.audioSessionActive.get(), video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
let disposable = MetaDisposable() let disposable = MetaDisposable()
@ -1860,7 +1854,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
preferMuted: true, preferMuted: true,
joinPayload: joinPayload, joinPayload: joinPayload,
peerAdminIds: peerAdminIds, peerAdminIds: peerAdminIds,
inviteHash: strongSelf.invite inviteHash: strongSelf.invite,
keyFingerprint: strongSelf.encryptionKey?.fingerprint
) )
|> deliverOnMainQueue).start(next: { joinCallResult in |> deliverOnMainQueue).start(next: { joinCallResult in
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -126,7 +126,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
switch messsage { switch messsage {
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
let chatPeerId = messagePeerId let chatPeerId = messagePeerId
return chatPeerId.peerId return chatPeerId.peerId
case let .messageEmpty(_, _, peerId): case let .messageEmpty(_, _, peerId):
@ -142,7 +142,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
switch message { switch message {
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _, _):
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
var result = [peerId] var result = [peerId]
@ -270,7 +270,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
switch message { switch message {
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
if let replyTo = replyTo { if let replyTo = replyTo {
let peerId: PeerId = chatPeerId.peerId let peerId: PeerId = chatPeerId.peerId
@ -654,7 +654,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
extension StoreMessage { extension StoreMessage {
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
switch apiMessage { switch apiMessage {
case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck): case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck, _):
var attributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = []
if (flags2 & (1 << 4)) != 0 { if (flags2 & (1 << 4)) != 0 {

View File

@ -200,6 +200,8 @@ extension PhoneCallDiscardReason {
self = .hangup self = .hangup
case .phoneCallDiscardReasonMissed: case .phoneCallDiscardReasonMissed:
self = .missed self = .missed
case .phoneCallDiscardReasonAllowGroupCall:
self = .hangup
} }
} }
} }

View File

@ -134,19 +134,22 @@ public final class AccountStateManager {
public let timestamp: Int32 public let timestamp: Int32
public let peer: EnginePeer public let peer: EnginePeer
public let isVideo: Bool public let isVideo: Bool
public let isConference: Bool
init( init(
callId: Int64, callId: Int64,
callAccessHash: Int64, callAccessHash: Int64,
timestamp: Int32, timestamp: Int32,
peer: EnginePeer, peer: EnginePeer,
isVideo: Bool isVideo: Bool,
isConference: Bool
) { ) {
self.callId = callId self.callId = callId
self.callAccessHash = callAccessHash self.callAccessHash = callAccessHash
self.timestamp = timestamp self.timestamp = timestamp
self.peer = peer self.peer = peer
self.isVideo = isVideo self.isVideo = isVideo
self.isConference = isConference
} }
} }
@ -2160,7 +2163,7 @@ public final class AccountStateManager {
switch update { switch update {
case let .updatePhoneCall(phoneCall): case let .updatePhoneCall(phoneCall):
switch phoneCall { switch phoneCall {
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, _, _): case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, _, _, conferenceCall):
guard let peer = peers.first(where: { $0.id == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) }) else { guard let peer = peers.first(where: { $0.id == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) }) else {
return nil return nil
} }
@ -2169,7 +2172,8 @@ public final class AccountStateManager {
callAccessHash: accessHash, callAccessHash: accessHash,
timestamp: date, timestamp: date,
peer: EnginePeer(peer), peer: EnginePeer(peer),
isVideo: (flags & (1 << 6)) != 0 isVideo: (flags & (1 << 6)) != 0,
isConference: conferenceCall != nil
) )
default: default:
break break

View File

@ -104,7 +104,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
var updatedTimestamp: Int32? var updatedTimestamp: Int32?
if let apiMessage = apiMessage { if let apiMessage = apiMessage {
switch apiMessage { switch apiMessage {
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
updatedTimestamp = date updatedTimestamp = date
case .messageEmpty: case .messageEmpty:
break break
@ -400,7 +400,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage
} else if let message = messages.first, let apiMessage = result.messages.first { } else if let message = messages.first, let apiMessage = result.messages.first {
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
namespace = Namespaces.Message.ScheduledCloud namespace = Namespaces.Message.ScheduledCloud
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
namespace = Namespaces.Message.ScheduledCloud namespace = Namespaces.Message.ScheduledCloud
} }
} }

View File

@ -73,32 +73,35 @@ extension GroupCallReference {
} }
enum CallSessionInternalState { enum CallSessionInternalState {
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String]) case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String], conferenceCall: GroupCallReference?)
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable) case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig) case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
case requesting(a: Data, disposable: Disposable) case requesting(a: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?) case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?, conferenceCall: GroupCallReference?)
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable) case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?) case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
case dropping(reason: CallSessionTerminationReason, disposable: Disposable) case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool) case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
var stableId: Int64? { var stableId: Int64? {
switch self { switch self {
case let .ringing(id, _, _, _, _): case let .ringing(id, _, _, _, _, _):
return id return id
case let .accepting(id, _, _, _, _): case let .accepting(id, _, _, _, _, _):
return id return id
case let .awaitingConfirmation(id, _, _, _, _): case let .awaitingConfirmation(id, _, _, _, _):
return id return id
case .requesting: case .requesting:
return nil return nil
case let .requested(id, _, _, _, _, _): case let .requested(id, _, _, _, _, _, _):
return id return id
case let .confirming(id, _, _, _, _, _): case let .confirming(id, _, _, _, _, _, _):
return id return id
case let .active(id, _, _, _, _, _, _, _, _, _, _, _): case let .active(id, _, _, _, _, _, _, _, _, _, _, _):
return id return id
case .switchedToConference:
return nil
case .dropping: case .dropping:
return nil return nil
case let .terminated(id, _, _, _, _): case let .terminated(id, _, _, _, _):
@ -161,8 +164,9 @@ public struct CallTerminationOptions: OptionSet {
public enum CallSessionState { public enum CallSessionState {
case ringing case ringing
case accepting case accepting
case requesting(ringing: Bool) case requesting(ringing: Bool, conferenceCall: GroupCallReference?)
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?) case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
case dropping(reason: CallSessionTerminationReason) case dropping(reason: CallSessionTerminationReason)
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions) case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
@ -172,12 +176,12 @@ public enum CallSessionState {
self = .ringing self = .ringing
case .accepting, .awaitingConfirmation: case .accepting, .awaitingConfirmation:
self = .accepting self = .accepting
case .requesting: case let .requesting(_, conferenceCall, _):
self = .requesting(ringing: false) self = .requesting(ringing: false, conferenceCall: conferenceCall)
case .confirming: case let .confirming(_, _, _, _, _, conferenceCall, _):
self = .requesting(ringing: true) self = .requesting(ringing: true, conferenceCall: conferenceCall)
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp): case let .requested(_, _, _, _, _, remoteConfirmationTimestamp, conferenceCall):
self = .requesting(ringing: remoteConfirmationTimestamp != nil) self = .requesting(ringing: remoteConfirmationTimestamp != nil, conferenceCall: conferenceCall)
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall): case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall) self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall)
case let .dropping(reason, _): case let .dropping(reason, _):
@ -197,6 +201,8 @@ public enum CallSessionState {
callId = nil callId = nil
} }
self = .terminated(id: callId, reason: reason, options: options) self = .terminated(id: callId, reason: reason, options: options)
case let .switchedToConference(key, keyVisualHash, conferenceCall):
self = .switchedToConference(key: key, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall)
} }
} }
} }
@ -330,6 +336,7 @@ private final class CallSessionContext {
let isOutgoing: Bool let isOutgoing: Bool
var type: CallSession.CallType var type: CallSession.CallType
var isVideoPossible: Bool var isVideoPossible: Bool
let pendingConference: (conference: GroupCallReference, encryptionKey: Data)?
var state: CallSessionInternalState var state: CallSessionInternalState
let subscribers = Bag<(CallSession) -> Void>() let subscribers = Bag<(CallSession) -> Void>()
var signalingReceiver: (([Data]) -> Void)? var signalingReceiver: (([Data]) -> Void)?
@ -346,11 +353,12 @@ private final class CallSessionContext {
} }
} }
init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, state: CallSessionInternalState) { init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
self.peerId = peerId self.peerId = peerId
self.isOutgoing = isOutgoing self.isOutgoing = isOutgoing
self.type = type self.type = type
self.isVideoPossible = isVideoPossible self.isVideoPossible = isVideoPossible
self.pendingConference = pendingConference
self.state = state self.state = state
} }
@ -566,7 +574,7 @@ private final class CallSessionManagerContext {
} }
} }
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool) -> CallSessionInternalId? { private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> CallSessionInternalId? {
if self.contextIdByStableId[stableId] != nil { if self.contextIdByStableId[stableId] != nil {
return nil return nil
} }
@ -582,7 +590,7 @@ private final class CallSessionManagerContext {
//#endif //#endif
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId) let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions)) let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
self.contexts[internalId] = context self.contexts[internalId] = context
let queue = self.queue let queue = self.queue
@ -619,7 +627,7 @@ private final class CallSessionManagerContext {
var wasRinging = false var wasRinging = false
let isVideo = context.type == .video let isVideo = context.type == .video
switch context.state { switch context.state {
case let .ringing(id, accessHash, _, _, _): case let .ringing(id, accessHash, _, _, _, _):
wasRinging = true wasRinging = true
let internalReason: DropCallSessionReason let internalReason: DropCallSessionReason
switch reason { switch reason {
@ -633,7 +641,7 @@ private final class CallSessionManagerContext {
internalReason = .missed internalReason = .missed
} }
dropData = (id, accessHash, internalReason) dropData = (id, accessHash, internalReason)
case let .accepting(id, accessHash, _, _, disposable): case let .accepting(id, accessHash, _, _, _, disposable):
dropData = (id, accessHash, .abort) dropData = (id, accessHash, .abort)
disposable.dispose() disposable.dispose()
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _): case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _):
@ -648,14 +656,16 @@ private final class CallSessionManagerContext {
internalReason = .missed internalReason = .missed
} }
dropData = (id, accessHash, internalReason) dropData = (id, accessHash, internalReason)
case .switchedToConference:
break
case .dropping, .terminated: case .dropping, .terminated:
break break
case let .awaitingConfirmation(id, accessHash, _, _, _): case let .awaitingConfirmation(id, accessHash, _, _, _):
dropData = (id, accessHash, .abort) dropData = (id, accessHash, .abort)
case let .confirming(id, accessHash, _, _, _, disposable): case let .confirming(id, accessHash, _, _, _, _, disposable):
disposable.dispose() disposable.dispose()
dropData = (id, accessHash, .abort) dropData = (id, accessHash, .abort)
case let .requested(id, accessHash, _, _, _, _): case let .requested(id, accessHash, _, _, _, _, _):
let internalReason: DropCallSessionReason let internalReason: DropCallSessionReason
switch reason { switch reason {
case .busy, .hangUp: case .busy, .hangUp:
@ -666,7 +676,7 @@ private final class CallSessionManagerContext {
internalReason = .missed internalReason = .missed
} }
dropData = (id, accessHash, internalReason) dropData = (id, accessHash, internalReason)
case let .requesting(_, disposable): case let .requesting(_, _, disposable):
disposable.dispose() disposable.dispose()
context.state = .terminated(id: nil, accessHash: nil, reason: .ended(.hungUp), reportRating: false, sendDebugLogs: false) context.state = .terminated(id: nil, accessHash: nil, reason: .ended(.hungUp), reportRating: false, sendDebugLogs: false)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
@ -689,6 +699,8 @@ private final class CallSessionManagerContext {
mappedReason = .ended(.hungUp) mappedReason = .ended(.hungUp)
case .missed: case .missed:
mappedReason = .ended(.missed) mappedReason = .ended(.missed)
case .switchToConference:
mappedReason = .ended(.hungUp)
} }
context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason) context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in |> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
@ -729,6 +741,38 @@ private final class CallSessionManagerContext {
} }
} }
func dropToConference(internalId: CallSessionInternalId, encryptedGroupKey: Data) {
if let context = self.contexts[internalId] {
var dropData: (CallSessionStableId, Int64)?
let isVideo = context.type == .video
switch context.state {
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
dropData = (id, accessHash)
default:
break
}
if let (id, accessHash) = dropData {
self.contextIdByStableId.removeValue(forKey: id)
context.state = .dropping(reason: .ended(.hungUp), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey))
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
if let strongSelf = self {
if let context = strongSelf.contexts[internalId] {
context.state = .terminated(id: id, accessHash: accessHash, reason: .ended(.hungUp), reportRating: reportRating, sendDebugLogs: sendDebugLogs)
strongSelf.contextUpdated(internalId: internalId)
if context.isEmpty {
strongSelf.contexts.removeValue(forKey: internalId)
}
}
}
}))
self.contextUpdated(internalId: internalId)
}
} else {
self.contextUpdated(internalId: internalId)
}
}
func dropAll() { func dropAll() {
let contexts = self.contexts let contexts = self.contexts
for (internalId, _) in contexts { for (internalId, _) in contexts {
@ -739,9 +783,9 @@ private final class CallSessionManagerContext {
func accept(internalId: CallSessionInternalId) { func accept(internalId: CallSessionInternalId) {
if let context = self.contexts[internalId] { if let context = self.contexts[internalId] {
switch context.state { switch context.state {
case let .ringing(id, accessHash, gAHash, b, _): case let .ringing(id, accessHash, gAHash, b, _, conferenceCall):
let acceptVersions = self.versions.map({ $0.version }) let acceptVersions = self.versions.map({ $0.version })
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, conferenceCall: conferenceCall, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
if let strongSelf = self, let context = strongSelf.contexts[internalId] { if let strongSelf = self, let context = strongSelf.contexts[internalId] {
if case .accepting = context.state { if case .accepting = context.state {
switch result { switch result {
@ -835,7 +879,7 @@ private final class CallSessionManagerContext {
switch call { switch call {
case .phoneCallEmpty: case .phoneCallEmpty:
break break
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol): case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol, conferenceCall):
let remoteVersions: [String] let remoteVersions: [String]
switch remoteProtocol { switch remoteProtocol {
case let .phoneCallProtocol(_, _, _, versions): case let .phoneCallProtocol(_, _, _, versions):
@ -849,7 +893,7 @@ private final class CallSessionManagerContext {
if let context = self.contexts[internalId] { if let context = self.contexts[internalId] {
switch context.state { switch context.state {
case let .requested(_, accessHash, a, gA, config, _): case let .requested(_, accessHash, a, gA, config, _, _):
let p = config.p.makeData() let p = config.p.makeData()
if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) { if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) {
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
@ -874,10 +918,14 @@ private final class CallSessionManagerContext {
let keyVisualHash = MTSha256(key + gA) let keyVisualHash = MTSha256(key + gA)
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state { if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
if let updatedCall = updatedCall { if let updatedCall = updatedCall {
strongSelf.updateSession(updatedCall, completion: { _ in }) strongSelf.updateSession(updatedCall, completion: { _ in })
if let pendingConference = context.pendingConference {
strongSelf.dropToConference(internalId: internalId, encryptedGroupKey: pendingConference.encryptionKey)
}
} else { } else {
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
} }
@ -891,7 +939,8 @@ private final class CallSessionManagerContext {
assertionFailure() assertionFailure()
} }
} }
case let .phoneCallDiscarded(flags, id, reason, _): case let .phoneCallDiscarded(flags, id, reason, _, conferenceCall):
let _ = conferenceCall
let reportRating = (flags & (1 << 2)) != 0 let reportRating = (flags & (1 << 2)) != 0
let sendDebugLogs = (flags & (1 << 3)) != 0 let sendDebugLogs = (flags & (1 << 3)) != 0
if let internalId = self.contextIdByStableId[id] { if let internalId = self.contextIdByStableId[id] {
@ -907,38 +956,44 @@ private final class CallSessionManagerContext {
parsedReason = .ended(.hungUp) parsedReason = .ended(.hungUp)
case .phoneCallDiscardReasonMissed: case .phoneCallDiscardReasonMissed:
parsedReason = .ended(.missed) parsedReason = .ended(.missed)
case .phoneCallDiscardReasonAllowGroupCall:
parsedReason = .ended(.hungUp)
} }
} else { } else {
parsedReason = .ended(.hungUp) parsedReason = .ended(.hungUp)
} }
switch context.state { switch context.state {
case let .accepting(id, accessHash, _, _, disposable): case let .accepting(id, accessHash, _, _, _, disposable):
disposable.dispose() disposable.dispose()
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _): case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall):
if let conferenceCall, case let .phoneCallDiscardReasonAllowGroupCall(encryptedGroupKey) = reason {
context.state = .switchedToConference(key: encryptedGroupKey.makeData(), keyVisualHash: MTSha256(encryptedGroupKey.makeData()), conferenceCall: conferenceCall)
} else {
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
}
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .awaitingConfirmation(id, accessHash, _, _, _): case let .awaitingConfirmation(id, accessHash, _, _, _):
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .requested(id, accessHash, _, _, _, _): case let .requested(id, accessHash, _, _, _, _, _):
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .confirming(id, accessHash, _, _, _, disposable): case let .confirming(id, accessHash, _, _, _, _, disposable):
disposable.dispose() disposable.dispose()
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .requesting(_, disposable): case let .requesting(_, _, disposable):
disposable.dispose() disposable.dispose()
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false) context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case let .ringing(id, accessHash, _, _, _): case let .ringing(id, accessHash, _, _, _, _):
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs) context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
self.ringingStatesUpdated() self.ringingStatesUpdated()
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
case .dropping, .terminated: case .dropping, .terminated, .switchedToConference:
break break
} }
} else { } else {
@ -950,7 +1005,7 @@ private final class CallSessionManagerContext {
if let internalId = self.contextIdByStableId[id] { if let internalId = self.contextIdByStableId[id] {
if let context = self.contexts[internalId] { if let context = self.contexts[internalId] {
switch context.state { switch context.state {
case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested: case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
break break
case let .awaitingConfirmation(_, accessHash, gAHash, b, config): case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) { if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
@ -981,7 +1036,7 @@ private final class CallSessionManagerContext {
} else { } else {
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil)) self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
} }
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _): case let .confirming(id, accessHash, key, keyId, keyVisualHash, _, _):
switch callProtocol { switch callProtocol {
case let .phoneCallProtocol(_, _, maxLayer, versions): case let .phoneCallProtocol(_, _, maxLayer, versions):
if !versions.isEmpty { if !versions.isEmpty {
@ -1007,7 +1062,7 @@ private final class CallSessionManagerContext {
assertionFailure() assertionFailure()
} }
} }
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol): case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol, conferenceCall):
let isVideo = (flags & (1 << 6)) != 0 let isVideo = (flags & (1 << 6)) != 0
let versions: [String] let versions: [String]
switch requestedProtocol { switch requestedProtocol {
@ -1015,7 +1070,7 @@ private final class CallSessionManagerContext {
versions = libraryVersions versions = libraryVersions
} }
if self.contextIdByStableId[id] == nil { if self.contextIdByStableId[id] == nil {
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo) let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
if let internalId = internalId { if let internalId = internalId {
var resultRingingStateValue: CallSessionRingingState? var resultRingingStateValue: CallSessionRingingState?
for ringingState in self.ringingStatesValue() { for ringingState in self.ringingStatesValue() {
@ -1032,13 +1087,13 @@ private final class CallSessionManagerContext {
} }
} }
} }
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate): case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate, conferenceCall):
if let internalId = self.contextIdByStableId[id] { if let internalId = self.contextIdByStableId[id] {
if let context = self.contexts[internalId] { if let context = self.contexts[internalId] {
switch context.state { switch context.state {
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp): case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp, _):
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil { if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate) context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
self.contextUpdated(internalId: internalId) self.contextUpdated(internalId: internalId)
} }
default: default:
@ -1101,17 +1156,17 @@ private final class CallSessionManagerContext {
return (key, keyId, keyVisualHash) return (key, keyId, keyVisualHash)
} }
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool) -> CallSessionInternalId? { func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?) -> CallSessionInternalId? {
let aBytes = malloc(256)! let aBytes = malloc(256)!
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self)) let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free) let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
if randomStatus == 0 { if randomStatus == 0 {
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, state: .requesting(a: a, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo) |> deliverOn(queue)).start(next: { [weak self] result in self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, pendingConference: conferenceCall, state: .requesting(a: a, conferenceCall: conferenceCall?.conference, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo, conferenceCall: conferenceCall?.conference) |> deliverOn(queue)).start(next: { [weak self] result in
if let strongSelf = self, let context = strongSelf.contexts[internalId] { if let strongSelf = self, let context = strongSelf.contexts[internalId] {
if case .requesting = context.state { if case .requesting = context.state {
switch result { switch result {
case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp): case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp):
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp) context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp, conferenceCall: conferenceCall?.conference)
strongSelf.contextIdByStableId[id] = internalId strongSelf.contextIdByStableId[id] = internalId
strongSelf.contextUpdated(internalId: internalId) strongSelf.contextUpdated(internalId: internalId)
strongSelf.deliverCallSignalingData(id: id) strongSelf.deliverCallSignalingData(id: id)
@ -1206,12 +1261,12 @@ public final class CallSessionManager {
} }
} }
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> { public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
return Signal { [weak self] subscriber in return Signal { [weak self] subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self?.withContext { context in self?.withContext { context in
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo) { if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo, conferenceCall: conferenceCall) {
subscriber.putNext(internalId) subscriber.putNext(internalId)
subscriber.putCompletion() subscriber.putCompletion()
} }
@ -1358,7 +1413,7 @@ private enum RequestCallSessionResult {
case failed(CallSessionError) case failed(CallSessionError)
} }
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool) -> Signal<RequestCallSessionResult, NoError> { private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> Signal<RequestCallSessionResult, NoError> {
return validatedEncryptionConfig(postbox: postbox, network: network) return validatedEncryptionConfig(postbox: postbox, network: network)
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in |> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
@ -1378,15 +1433,18 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
if isVideo { if isVideo {
callFlags |= 1 << 0 callFlags |= 1 << 0
} }
if conferenceCall != nil {
callFlags |= 1 << 1
}
return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions))) return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, conferenceCall: conferenceCall.flatMap { Api.InputGroupCall.inputGroupCall(id: $0.id, accessHash: $0.accessHash) }, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions)))
|> map { result -> RequestCallSessionResult in |> map { result -> RequestCallSessionResult in
switch result { switch result {
case let .phoneCall(phoneCall, _): case let .phoneCall(phoneCall, _):
switch phoneCall { switch phoneCall {
case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _): case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _, _):
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil) return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate): case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate, _):
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate) return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
default: default:
return .failed(.generic) return .failed(.generic)
@ -1439,6 +1497,7 @@ private enum DropCallSessionReason {
case busy case busy
case disconnect case disconnect
case missed case missed
case switchToConference(encryptedGroupKey: Data)
} }
private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> { private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> {
@ -1456,6 +1515,8 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
mappedReason = .phoneCallDiscardReasonDisconnect mappedReason = .phoneCallDiscardReasonDisconnect
case .missed: case .missed:
mappedReason = .phoneCallDiscardReasonMissed mappedReason = .phoneCallDiscardReasonMissed
case let .switchToConference(encryptedGroupKey):
mappedReason = .phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer(data: encryptedGroupKey))
} }
var callFlags: Int32 = 0 var callFlags: Int32 = 0
@ -1478,7 +1539,7 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
switch update { switch update {
case .updatePhoneCall(let phoneCall): case .updatePhoneCall(let phoneCall):
switch phoneCall { switch phoneCall {
case let .phoneCallDiscarded(flags, _, _, _): case let .phoneCallDiscarded(flags, _, _, _, _):
reportRating = (flags & (1 << 2)) != 0 reportRating = (flags & (1 << 2)) != 0
sendDebugLogs = (flags & (1 << 3)) != 0 sendDebugLogs = (flags & (1 << 3)) != 0
default: default:
@ -1501,33 +1562,7 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
} }
private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> { private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> {
#if DEBUG && false return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash), keyFingerprint: 1))
if "".isEmpty {
return _internal_resolvePeerByName(postbox: postbox, network: network, accountPeerId: accountPeerId, name: "qwfqwfqwefqwef22", referrer: nil)
|> mapToSignal { result -> Signal<PeerId?, NoError> in
switch result {
case .progress:
return .never()
case let .result(peerId):
return .single(peerId)
}
}
|> mapToSignal { peerId -> Signal<GroupCallReference?, NoError> in
guard let peerId else {
return .single(nil)
}
return _internal_updatedCurrentPeerGroupCall(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId)
|> map { result -> GroupCallReference? in
guard let result else {
return nil
}
return GroupCallReference(id: result.id, accessHash: result.accessHash)
}
}
}
#endif
return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash)))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in |> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
return .single(nil) return .single(nil)

View File

@ -1745,7 +1745,7 @@ public final class PendingMessageManager {
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
isScheduled = true isScheduled = true
} }
if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage { if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage {
if (flags2 & (1 << 4)) != 0 { if (flags2 & (1 << 4)) != 0 {
isScheduled = true isScheduled = true
} }
@ -1780,7 +1780,7 @@ public final class PendingMessageManager {
namespace = Namespaces.Message.QuickReplyCloud namespace = Namespaces.Message.QuickReplyCloud
} else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { } else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
namespace = Namespaces.Message.ScheduledCloud namespace = Namespaces.Message.ScheduledCloud
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
namespace = Namespaces.Message.ScheduledCloud namespace = Namespaces.Message.ScheduledCloud
} }
} }

View File

@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
self.putNext(groups) self.putNext(groups)
} }
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod): case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod):
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil) let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 { if groups.count != 0 {
@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService {
let generatedPeerId = Api.Peer.peerUser(userId: userId) let generatedPeerId = Api.Peer.peerUser(userId: userId)
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil) let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 { if groups.count != 0 {

View File

@ -104,7 +104,7 @@ extension Api.MessageMedia {
extension Api.Message { extension Api.Message {
var rawId: Int32 { var rawId: Int32 {
switch self { switch self {
case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return id return id
case let .messageEmpty(_, id, _): case let .messageEmpty(_, id, _):
return id return id
@ -115,7 +115,7 @@ extension Api.Message {
func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? { func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? {
switch self { switch self {
case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
var namespace = namespace var namespace = namespace
if (flags2 & (1 << 4)) != 0 { if (flags2 & (1 << 4)) != 0 {
namespace = Namespaces.Message.ScheduledCloud namespace = Namespaces.Message.ScheduledCloud
@ -136,7 +136,7 @@ extension Api.Message {
var peerId: PeerId? { var peerId: PeerId? {
switch self { switch self {
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
let peerId: PeerId = messagePeerId.peerId let peerId: PeerId = messagePeerId.peerId
return peerId return peerId
case let .messageEmpty(_, _, peerId): case let .messageEmpty(_, _, peerId):
@ -149,7 +149,7 @@ extension Api.Message {
var timestamp: Int32? { var timestamp: Int32? {
switch self { switch self {
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return date return date
case let .messageService(_, _, _, _, _, date, _, _, _): case let .messageService(_, _, _, _, _, date, _, _, _):
return date return date
@ -160,7 +160,7 @@ extension Api.Message {
var preCachedResources: [(MediaResource, Data)]? { var preCachedResources: [(MediaResource, Data)]? {
switch self { switch self {
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return media?.preCachedResources return media?.preCachedResources
default: default:
return nil return nil
@ -169,7 +169,7 @@ extension Api.Message {
var preCachedStories: [StoryId: Api.StoryItem]? { var preCachedStories: [StoryId: Api.StoryItem]? {
switch self { switch self {
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return media?.preCachedStories return media?.preCachedStories
default: default:
return nil return nil

View File

@ -435,7 +435,7 @@ public struct JoinGroupCallResult {
public var jsonParams: String public var jsonParams: String
} }
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> { func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, keyFingerprint: Int64?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in return account.postbox.transaction { transaction -> Api.InputPeer? in
if let joinAs = joinAs { if let joinAs = joinAs {
return transaction.getPeer(joinAs).flatMap(apiInputPeer) return transaction.getPeer(joinAs).flatMap(apiInputPeer)
@ -457,8 +457,11 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
if let _ = inviteHash { if let _ = inviteHash {
flags |= (1 << 1) flags |= (1 << 1)
} }
if keyFingerprint != nil {
flags |= (1 << 3)
}
let joinRequest = account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, inviteHash: inviteHash, params: .dataJSON(data: joinPayload))) let joinRequest = account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, inviteHash: inviteHash, keyFingerprint: keyFingerprint, params: .dataJSON(data: joinPayload)))
|> `catch` { error -> Signal<Api.Updates, JoinGroupCallError> in |> `catch` { error -> Signal<Api.Updates, JoinGroupCallError> in
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" { if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
return .fail(.anonymousNotAllowed) return .fail(.anonymousNotAllowed)

View File

@ -57,8 +57,8 @@ public extension TelegramEngine {
return _internal_getGroupCallParticipants(account: self.account, callId: callId, accessHash: accessHash, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending) return _internal_getGroupCallParticipants(account: self.account, callId: callId, accessHash: accessHash, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
} }
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> { public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, keyFingerprint: Int64?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, accessHash: accessHash, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash) return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, accessHash: accessHash, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, keyFingerprint: keyFingerprint)
} }
public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> { public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {

View File

@ -226,6 +226,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
public var backAction: (() -> Void)? public var backAction: (() -> Void)?
public var closeAction: (() -> Void)? public var closeAction: (() -> Void)?
public var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)? public var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)?
public var conferenceAddParticipant: (() -> Void)?
private let pipView: PrivateCallPictureInPictureView private let pipView: PrivateCallPictureInPictureView
private var pipContentSource: AnyObject? private var pipContentSource: AnyObject?

@ -1 +1 @@
Subproject commit 965c46f32425cb270e88ab0aab7c3593b5be574e Subproject commit ab50f4e095d5793c39dc54b740a982fc3ba27ea5