mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] General UI improvements
This commit is contained in:
parent
f05031cc9d
commit
ff22c7e981
@ -464,7 +464,7 @@ private struct NotificationContent: CustomStringConvertible {
|
||||
return string
|
||||
}
|
||||
|
||||
mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) {
|
||||
mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer, contactIdentifier: String?) {
|
||||
if #available(iOS 15.0, *) {
|
||||
let image = peerAvatar(mediaBox: mediaBox, accountPeerId: accountPeerId, peer: peer)
|
||||
|
||||
@ -483,7 +483,7 @@ private struct NotificationContent: CustomStringConvertible {
|
||||
nameComponents: personNameComponents,
|
||||
displayName: displayName,
|
||||
image: image,
|
||||
contactIdentifier: nil,
|
||||
contactIdentifier: contactIdentifier,
|
||||
customIdentifier: "\(peer.id.toInt64())",
|
||||
isMe: false,
|
||||
suggestionType: .none
|
||||
@ -818,6 +818,7 @@ private final class NotificationServiceHandler {
|
||||
var updates: String
|
||||
var accountId: Int64
|
||||
var peer: EnginePeer?
|
||||
var localContactId: String?
|
||||
}
|
||||
|
||||
var callData: CallData?
|
||||
@ -1033,35 +1034,51 @@ private final class NotificationServiceHandler {
|
||||
if let action = action {
|
||||
switch action {
|
||||
case let .call(callData):
|
||||
let voipPayload: [AnyHashable: Any] = [
|
||||
"call_id": "\(callData.id)",
|
||||
"call_ah": "\(callData.accessHash)",
|
||||
"from_id": "\(callData.fromId.id._internalGetInt64Value())",
|
||||
"updates": callData.updates,
|
||||
"accountId": "\(callData.accountId)"
|
||||
]
|
||||
|
||||
if #available(iOS 14.5, *), voiceCallSettings.enableSystemIntegration {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will report voip notification")
|
||||
if let stateManager = strongSelf.stateManager {
|
||||
let content = NotificationContent(isLockedMessage: nil)
|
||||
updateCurrentContent(content)
|
||||
|
||||
CXProvider.reportNewIncomingVoIPPushPayload(voipPayload, completion: { error in
|
||||
Logger.shared.log("NotificationService \(episode)", "Did report voip notification, error: \(String(describing: error))")
|
||||
let _ = (stateManager.postbox.transaction { transaction -> String? in
|
||||
if let peer = transaction.getPeer(callData.fromId) as? TelegramUser {
|
||||
return peer.phone
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}).start(next: { phoneNumber in
|
||||
var voipPayload: [AnyHashable: Any] = [
|
||||
"call_id": "\(callData.id)",
|
||||
"call_ah": "\(callData.accessHash)",
|
||||
"from_id": "\(callData.fromId.id._internalGetInt64Value())",
|
||||
"updates": callData.updates,
|
||||
"accountId": "\(callData.accountId)"
|
||||
]
|
||||
if let phoneNumber = phoneNumber {
|
||||
voipPayload["phoneNumber"] = phoneNumber
|
||||
}
|
||||
|
||||
completed()
|
||||
if #available(iOS 14.5, *), voiceCallSettings.enableSystemIntegration {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will report voip notification")
|
||||
let content = NotificationContent(isLockedMessage: nil)
|
||||
updateCurrentContent(content)
|
||||
|
||||
CXProvider.reportNewIncomingVoIPPushPayload(voipPayload, completion: { error in
|
||||
Logger.shared.log("NotificationService \(episode)", "Did report voip notification, error: \(String(describing: error))")
|
||||
|
||||
completed()
|
||||
})
|
||||
} else {
|
||||
var content = NotificationContent(isLockedMessage: nil)
|
||||
if let peer = callData.peer {
|
||||
content.title = peer.debugDisplayTitle
|
||||
content.body = incomingCallMessage
|
||||
} else {
|
||||
content.body = "Incoming Call"
|
||||
}
|
||||
|
||||
updateCurrentContent(content)
|
||||
completed()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var content = NotificationContent(isLockedMessage: nil)
|
||||
if let peer = callData.peer {
|
||||
content.title = peer.debugDisplayTitle
|
||||
content.body = incomingCallMessage
|
||||
} else {
|
||||
content.body = "Incoming Call"
|
||||
}
|
||||
|
||||
updateCurrentContent(content)
|
||||
completed()
|
||||
}
|
||||
case .logout:
|
||||
Logger.shared.log("NotificationService \(episode)", "Will logout")
|
||||
@ -1334,7 +1351,23 @@ private final class NotificationServiceHandler {
|
||||
|
||||
if let interactionAuthorId = interactionAuthorId {
|
||||
if inAppNotificationSettings.displayNameOnLockscreen, let peer = transaction.getPeer(interactionAuthorId) {
|
||||
content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer)
|
||||
var foundLocalId: String?
|
||||
transaction.enumerateDeviceContactImportInfoItems({ _, value in
|
||||
if let value = value as? TelegramDeviceContactImportedData {
|
||||
switch value {
|
||||
case let .imported(data, _, peerId):
|
||||
if peerId == interactionAuthorId {
|
||||
foundLocalId = data.localIdentifiers.first
|
||||
return false
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer, contactIdentifier: foundLocalId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,7 +482,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
|
||||
for node in nodes {
|
||||
other.addSubnode(node)
|
||||
node.layer.animateAlpha(from: node.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node.layer.animateAlpha(from: node.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
@ -491,12 +491,11 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
func animateContentIn() {
|
||||
let nodes: [ASDisplayNode] = [
|
||||
self.textNode.textNode,
|
||||
self.iconNode,
|
||||
self.placeholderNode
|
||||
self.iconNode
|
||||
]
|
||||
|
||||
for node in nodes {
|
||||
node.layer.animateAlpha(from: 0.0, to: node.alpha, duration: 0.2)
|
||||
node.layer.animateAlpha(from: 0.0, to: node.alpha, duration: 0.25)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,6 +378,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
|
||||
let belowKeyboardOverlayLayout = layout
|
||||
var globalOverlayLayout = layout
|
||||
globalOverlayLayout.inputHeight = nil
|
||||
|
||||
if let globalOverlayBelowKeyboardContainerParent = self.globalOverlayBelowKeyboardContainerParent {
|
||||
if globalOverlayBelowKeyboardContainerParent.view.superview != self.displayNode.view {
|
||||
|
@ -53,6 +53,9 @@ private func transcribeAudio(path: String, locale: String) -> Signal<Transcripti
|
||||
let _ = try? FileManager.default.copyItem(atPath: path, toPath: tempFilePath)
|
||||
|
||||
let request = SFSpeechURLRecognitionRequest(url: URL(fileURLWithPath: tempFilePath))
|
||||
if #available(iOS 16.0, *) {
|
||||
request.addsPunctuation = true
|
||||
}
|
||||
request.requiresOnDeviceRecognition = speechRecognizer.supportsOnDeviceRecognition
|
||||
request.shouldReportPartialResults = true
|
||||
|
||||
|
@ -1334,9 +1334,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
for attribute in item.file.attributes {
|
||||
switch attribute {
|
||||
case let .CustomEmoji(_, alt, _):
|
||||
if !alt.isEmpty, let keyword = allEmoticons[alt] {
|
||||
if !item.file.isPremiumEmoji || hasPremium {
|
||||
if !item.file.isPremiumEmoji || hasPremium {
|
||||
if !alt.isEmpty, let keyword = allEmoticons[alt] {
|
||||
result.append((alt, item.file, keyword))
|
||||
} else if alt == query {
|
||||
result.append((alt, item.file, alt))
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -1345,12 +1347,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
for keyword in keywords {
|
||||
for emoticon in keyword.emoticons {
|
||||
result.append((emoticon, nil, keyword.keyword))
|
||||
}
|
||||
}
|
||||
|
||||
var items: [EmojiPagerContentComponent.Item] = []
|
||||
|
||||
var existingIds = Set<MediaId>()
|
||||
|
@ -730,10 +730,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1660637285] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
|
||||
dict[-875679776] = { return Api.StatsPercentValue.parse_statsPercentValue($0) }
|
||||
dict[1202287072] = { return Api.StatsURL.parse_statsURL($0) }
|
||||
dict[-50416996] = { return Api.StickerKeyword.parse_stickerKeyword($0) }
|
||||
dict[313694676] = { return Api.StickerPack.parse_stickerPack($0) }
|
||||
dict[768691932] = { return Api.StickerSet.parse_stickerSet($0) }
|
||||
dict[1678812626] = { return Api.StickerSetCovered.parse_stickerSetCovered($0) }
|
||||
dict[451763941] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) }
|
||||
dict[1087454222] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) }
|
||||
dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) }
|
||||
dict[-1609668650] = { return Api.Theme.parse_theme($0) }
|
||||
dict[-94849324] = { return Api.ThemeSettings.parse_themeSettings($0) }
|
||||
@ -1029,7 +1030,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
|
||||
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
|
||||
dict[1705297877] = { return Api.messages.SponsoredMessages.parse_sponsoredMessages($0) }
|
||||
dict[-1240849242] = { return Api.messages.StickerSet.parse_stickerSet($0) }
|
||||
dict[1846886166] = { return Api.messages.StickerSet.parse_stickerSet($0) }
|
||||
dict[-738646805] = { return Api.messages.StickerSet.parse_stickerSetNotModified($0) }
|
||||
dict[904138920] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultArchive($0) }
|
||||
dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) }
|
||||
@ -1584,6 +1585,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StatsURL:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StickerKeyword:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StickerPack:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StickerSet:
|
||||
|
@ -1,3 +1,49 @@
|
||||
public extension Api {
|
||||
enum StickerKeyword: TypeConstructorDescription {
|
||||
case stickerKeyword(documentId: Int64, keyword: [String])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerKeyword(let documentId, let keyword):
|
||||
if boxed {
|
||||
buffer.appendInt32(-50416996)
|
||||
}
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(keyword.count))
|
||||
for item in keyword {
|
||||
serializeString(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerKeyword(let documentId, let keyword):
|
||||
return ("stickerKeyword", [("documentId", String(describing: documentId)), ("keyword", String(describing: keyword))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerKeyword(_ reader: BufferReader) -> StickerKeyword? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [String]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerPack: TypeConstructorDescription {
|
||||
case stickerPack(emoticon: String, documents: [Int64])
|
||||
@ -133,7 +179,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
enum StickerSetCovered: TypeConstructorDescription {
|
||||
case stickerSetCovered(set: Api.StickerSet, cover: Api.Document)
|
||||
case stickerSetFullCovered(set: Api.StickerSet, packs: [Api.StickerPack], documents: [Api.Document])
|
||||
case stickerSetFullCovered(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document])
|
||||
case stickerSetMultiCovered(set: Api.StickerSet, covers: [Api.Document])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
@ -145,9 +191,9 @@ public extension Api {
|
||||
set.serialize(buffer, true)
|
||||
cover.serialize(buffer, true)
|
||||
break
|
||||
case .stickerSetFullCovered(let set, let packs, let documents):
|
||||
case .stickerSetFullCovered(let set, let packs, let keywords, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(451763941)
|
||||
buffer.appendInt32(1087454222)
|
||||
}
|
||||
set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -156,6 +202,11 @@ public extension Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(keywords.count))
|
||||
for item in keywords {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
item.serialize(buffer, true)
|
||||
@ -179,8 +230,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .stickerSetCovered(let set, let cover):
|
||||
return ("stickerSetCovered", [("set", String(describing: set)), ("cover", String(describing: cover))])
|
||||
case .stickerSetFullCovered(let set, let packs, let documents):
|
||||
return ("stickerSetFullCovered", [("set", String(describing: set)), ("packs", String(describing: packs)), ("documents", String(describing: documents))])
|
||||
case .stickerSetFullCovered(let set, let packs, let keywords, let documents):
|
||||
return ("stickerSetFullCovered", [("set", String(describing: set)), ("packs", String(describing: packs)), ("keywords", String(describing: keywords)), ("documents", String(describing: documents))])
|
||||
case .stickerSetMultiCovered(let set, let covers):
|
||||
return ("stickerSetMultiCovered", [("set", String(describing: set)), ("covers", String(describing: covers))])
|
||||
}
|
||||
@ -213,15 +264,20 @@ public extension Api {
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.Document]?
|
||||
var _3: [Api.StickerKeyword]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, documents: _3!)
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -638,14 +638,14 @@ public extension Api.messages {
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum StickerSet: TypeConstructorDescription {
|
||||
case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], documents: [Api.Document])
|
||||
case stickerSet(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document])
|
||||
case stickerSetNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerSet(let set, let packs, let documents):
|
||||
case .stickerSet(let set, let packs, let keywords, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1240849242)
|
||||
buffer.appendInt32(1846886166)
|
||||
}
|
||||
set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -654,6 +654,11 @@ public extension Api.messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(keywords.count))
|
||||
for item in keywords {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
item.serialize(buffer, true)
|
||||
@ -670,8 +675,8 @@ public extension Api.messages {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerSet(let set, let packs, let documents):
|
||||
return ("stickerSet", [("set", String(describing: set)), ("packs", String(describing: packs)), ("documents", String(describing: documents))])
|
||||
case .stickerSet(let set, let packs, let keywords, let documents):
|
||||
return ("stickerSet", [("set", String(describing: set)), ("packs", String(describing: packs)), ("keywords", String(describing: keywords)), ("documents", String(describing: documents))])
|
||||
case .stickerSetNotModified:
|
||||
return ("stickerSetNotModified", [])
|
||||
}
|
||||
@ -686,15 +691,20 @@ public extension Api.messages {
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.Document]?
|
||||
var _3: [Api.StickerKeyword]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, documents: _3!)
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -104,6 +104,7 @@ swift_library(
|
||||
"//submodules/Components/UndoPanelComponent:UndoPanelComponent",
|
||||
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
|
||||
"//submodules/PeerInfoUI/CreateExternalMediaStreamScreen:CreateExternalMediaStreamScreen",
|
||||
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -41,7 +41,7 @@ public final class CallKitIntegration {
|
||||
}
|
||||
|
||||
func setup(
|
||||
startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>,
|
||||
startCall: @escaping (AccountContext, UUID, EnginePeer.Id?, String, Bool) -> Signal<Bool, NoError>,
|
||||
answerCall: @escaping (UUID) -> Void,
|
||||
endCall: @escaping (UUID) -> Signal<Bool, NoError>,
|
||||
setCallMuted: @escaping (UUID, Bool) -> Void,
|
||||
@ -56,22 +56,17 @@ public final class CallKitIntegration {
|
||||
if !CallKitIntegration.isAvailable {
|
||||
return nil
|
||||
}
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
return nil
|
||||
#else
|
||||
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
||||
func startCall(context: AccountContext, peerId: PeerId, phoneNumber: String?, localContactId: String?, isVideo: Bool, displayTitle: String) {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: displayTitle)
|
||||
self.donateIntent(peerId: peerId, displayTitle: displayTitle)
|
||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(context: context, peerId: peerId, phoneNumber: phoneNumber, isVideo: isVideo, displayTitle: displayTitle)
|
||||
self.donateIntent(peerId: peerId, displayTitle: displayTitle, localContactId: localContactId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +82,9 @@ public final class CallKitIntegration {
|
||||
}
|
||||
}
|
||||
|
||||
public func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
|
||||
public func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, phoneNumber: String?, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.reportIncomingCall(uuid: uuid, stableId: stableId, handle: handle, isVideo: isVideo, displayTitle: displayTitle, completion: completion)
|
||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.reportIncomingCall(uuid: uuid, stableId: stableId, handle: handle, phoneNumber: phoneNumber, isVideo: isVideo, displayTitle: displayTitle, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,10 +94,10 @@ public final class CallKitIntegration {
|
||||
}
|
||||
}
|
||||
|
||||
private func donateIntent(peerId: PeerId, displayTitle: String) {
|
||||
private func donateIntent(peerId: PeerId, displayTitle: String, localContactId: String?) {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
let handle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown)
|
||||
let contact = INPerson(personHandle: handle, nameComponents: nil, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
|
||||
let handle = INPersonHandle(value: "tg\(peerId.id._internalGetInt64Value())", type: .unknown)
|
||||
let contact = INPerson(personHandle: handle, nameComponents: nil, displayName: displayTitle, image: nil, contactIdentifier: localContactId, customIdentifier: "tg\(peerId.id._internalGetInt64Value())")
|
||||
|
||||
let intent = INStartAudioCallIntent(contacts: [contact])
|
||||
|
||||
@ -122,8 +117,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
private var currentStartCallAccount: (UUID, AccountContext)?
|
||||
|
||||
private var alreadyReportedIncomingCalls = Set<UUID>()
|
||||
private var uuidToPeerIdMapping: [UUID: EnginePeer.Id] = [:]
|
||||
|
||||
private var startCall: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
||||
private var startCall: ((AccountContext, UUID, EnginePeer.Id?, String, Bool) -> Signal<Bool, NoError>)?
|
||||
private var answerCall: ((UUID) -> Void)?
|
||||
private var endCall: ((UUID) -> Signal<Bool, NoError>)?
|
||||
private var setCallMuted: ((UUID, Bool) -> Void)?
|
||||
@ -141,7 +137,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
self.provider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
||||
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (AccountContext, UUID, EnginePeer.Id?, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
||||
self.audioSessionActivePromise = audioSessionActivePromise
|
||||
self.startCall = startCall
|
||||
self.answerCall = answerCall
|
||||
@ -189,10 +185,18 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
self.requestTransaction(transaction)
|
||||
}
|
||||
|
||||
func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
||||
func startCall(context: AccountContext, peerId: PeerId, phoneNumber: String?, isVideo: Bool, displayTitle: String) {
|
||||
let uuid = UUID()
|
||||
self.currentStartCallAccount = (uuid, context)
|
||||
let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt64Value())")
|
||||
let handle: CXHandle
|
||||
if let phoneNumber = phoneNumber {
|
||||
handle = CXHandle(type: .phoneNumber, value: phoneNumber)
|
||||
} else {
|
||||
handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt64Value())")
|
||||
}
|
||||
|
||||
self.uuidToPeerIdMapping[uuid] = peerId
|
||||
|
||||
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
||||
startCallAction.contactIdentifier = displayTitle
|
||||
|
||||
@ -212,7 +216,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
})
|
||||
}
|
||||
|
||||
func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
|
||||
func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, phoneNumber: String?, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
|
||||
if self.alreadyReportedIncomingCalls.contains(uuid) {
|
||||
completion?(nil)
|
||||
return
|
||||
@ -220,7 +224,13 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
self.alreadyReportedIncomingCalls.insert(uuid)
|
||||
|
||||
let update = CXCallUpdate()
|
||||
update.remoteHandle = CXHandle(type: .generic, value: handle)
|
||||
let nativeHandle: CXHandle
|
||||
if let phoneNumber = phoneNumber {
|
||||
nativeHandle = CXHandle(type: .phoneNumber, value: phoneNumber)
|
||||
} else {
|
||||
nativeHandle = CXHandle(type: .generic, value: handle)
|
||||
}
|
||||
update.remoteHandle = nativeHandle
|
||||
update.localizedCallerName = displayTitle
|
||||
update.supportsHolding = false
|
||||
update.supportsGrouping = false
|
||||
@ -252,7 +262,10 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
self.currentStartCallAccount = nil
|
||||
let disposable = MetaDisposable()
|
||||
self.disposableSet.add(disposable)
|
||||
disposable.set((startCall(context, action.callUUID, action.handle.value, action.isVideo)
|
||||
|
||||
let peerId = self.uuidToPeerIdMapping[action.callUUID]
|
||||
|
||||
disposable.set((startCall(context, action.callUUID, peerId, action.handle.value, action.isVideo)
|
||||
|> deliverOnMainQueue
|
||||
|> afterDisposed { [weak self, weak disposable] in
|
||||
if let strongSelf = self, let disposable = disposable {
|
||||
|
@ -13,6 +13,7 @@ import DeviceAccess
|
||||
import UniversalMediaPlayer
|
||||
import AccountContext
|
||||
import DeviceProximity
|
||||
import PhoneNumberFormat
|
||||
|
||||
final class PresentationCallToneRenderer {
|
||||
let queue: Queue
|
||||
@ -607,10 +608,15 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if previous == nil || previousControl == nil {
|
||||
if !self.reportedIncomingCall, let stableId = sessionState.stableId {
|
||||
self.reportedIncomingCall = true
|
||||
var phoneNumber: String?
|
||||
if let peer = self.peer as? TelegramUser, let phone = peer.phone {
|
||||
phoneNumber = formatPhoneNumber(phone)
|
||||
}
|
||||
self.callKitIntegration?.reportIncomingCall(
|
||||
uuid: self.internalId,
|
||||
stableId: stableId,
|
||||
handle: "\(self.peerId.id._internalGetInt64Value())",
|
||||
phoneNumber: phoneNumber,
|
||||
isVideo: sessionState.type == .video,
|
||||
displayTitle: self.peer?.debugDisplayTitle ?? "Unknown",
|
||||
completion: { [weak self] error in
|
||||
|
@ -11,6 +11,7 @@ import TelegramVoip
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import CallKit
|
||||
import PhoneNumberFormat
|
||||
|
||||
private func callKitIntegrationIfEnabled(_ integration: CallKitIntegration?, settings: VoiceCallSettings?) -> CallKitIntegration? {
|
||||
let enabled = settings?.enableSystemIntegration ?? true
|
||||
@ -128,16 +129,16 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
self.isMediaPlaying = isMediaPlaying
|
||||
self.resumeMediaPlayback = resumeMediaPlayback
|
||||
|
||||
var startCallImpl: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
||||
var startCallImpl: ((AccountContext, UUID, EnginePeer.Id?, String, Bool) -> Signal<Bool, NoError>)?
|
||||
var answerCallImpl: ((UUID) -> Void)?
|
||||
var endCallImpl: ((UUID) -> Signal<Bool, NoError>)?
|
||||
var setCallMutedImpl: ((UUID, Bool) -> Void)?
|
||||
var audioSessionActivationChangedImpl: ((Bool) -> Void)?
|
||||
|
||||
self.callKitIntegration = CallKitIntegration.shared
|
||||
self.callKitIntegration?.setup(startCall: { context, uuid, handle, isVideo in
|
||||
self.callKitIntegration?.setup(startCall: { context, uuid, maybePeerId, handle, isVideo in
|
||||
if let startCallImpl = startCallImpl {
|
||||
return startCallImpl(context, uuid, handle, isVideo)
|
||||
return startCallImpl(context, uuid, maybePeerId, handle, isVideo)
|
||||
} else {
|
||||
return .single(false)
|
||||
}
|
||||
@ -215,16 +216,26 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
self?.ringingStatesUpdated(ringingStates, enableCallKit: enableCallKit)
|
||||
})
|
||||
|
||||
startCallImpl = { [weak self] context, uuid, handle, isVideo in
|
||||
if let strongSelf = self, let userId = Int64(handle) {
|
||||
return strongSelf.startCall(context: context, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), isVideo: isVideo, internalId: uuid)
|
||||
|> take(1)
|
||||
|> map { result -> Bool in
|
||||
return result
|
||||
}
|
||||
} else {
|
||||
startCallImpl = { [weak self] context, uuid, maybePeerId, handle, isVideo in
|
||||
guard let strongSelf = self else {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
var peerId: PeerId?
|
||||
if let maybePeerId = maybePeerId {
|
||||
peerId = maybePeerId
|
||||
} else if let userId = Int64(handle) {
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
}
|
||||
guard let peerId = peerId else {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
return strongSelf.startCall(context: context, peerId: peerId, isVideo: isVideo, internalId: uuid)
|
||||
|> take(1)
|
||||
|> map { result -> Bool in
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
answerCallImpl = { [weak self] uuid in
|
||||
@ -398,19 +409,39 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
|> runOn(Queue.mainQueue())
|
||||
let postbox = context.account.postbox
|
||||
strongSelf.startCallDisposable.set((accessEnabledSignal
|
||||
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
|
||||
|> mapToSignal { accessEnabled -> Signal<(Peer?, String?), NoError> in
|
||||
if !accessEnabled {
|
||||
return .single(nil)
|
||||
return .single((nil, nil))
|
||||
}
|
||||
return postbox.transaction { transaction -> (Peer?, String?) in
|
||||
var foundLocalId: String?
|
||||
transaction.enumerateDeviceContactImportInfoItems({ _, value in
|
||||
if let value = value as? TelegramDeviceContactImportedData {
|
||||
switch value {
|
||||
case let .imported(data, _, importedPeerId):
|
||||
if importedPeerId == peerId {
|
||||
foundLocalId = data.localIdentifiers.first
|
||||
return false
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return (transaction.getPeer(peerId), foundLocalId)
|
||||
}
|
||||
return postbox.loadedPeerWithId(peerId)
|
||||
|> take(1)
|
||||
|> map(Optional.init)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
|> deliverOnMainQueue).start(next: { peer, localContactId in
|
||||
guard let strongSelf = self, let peer = peer else {
|
||||
return
|
||||
}
|
||||
strongSelf.callKitIntegration?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle)
|
||||
var phoneNumber: String?
|
||||
if let peer = peer as? TelegramUser, let phone = peer.phone {
|
||||
phoneNumber = formatPhoneNumber(phone)
|
||||
}
|
||||
strongSelf.callKitIntegration?.startCall(context: context, peerId: peerId, phoneNumber: phoneNumber, localContactId: localContactId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle)
|
||||
}))
|
||||
}
|
||||
if let currentCall = self.currentCall {
|
||||
|
@ -3604,7 +3604,7 @@ func replayFinalState(
|
||||
let namespace: ItemCollectionId.Namespace
|
||||
var items: [ItemCollectionItem] = []
|
||||
let info: StickerPackCollectionInfo
|
||||
if case let .stickerSet(set, packs, documents) = apiSet {
|
||||
if case let .stickerSet(set, packs, keywords, documents) = apiSet {
|
||||
var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:]
|
||||
for pack in packs {
|
||||
switch pack {
|
||||
@ -3621,6 +3621,20 @@ func replayFinalState(
|
||||
break
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for apiDocument in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
|
||||
|
@ -1193,7 +1193,7 @@ public final class AccountViewTracker {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
for result in results {
|
||||
switch result {
|
||||
case let .stickerSet(_, _, documents)?:
|
||||
case let .stickerSet(_, _, _, documents)?:
|
||||
for document in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(document) {
|
||||
if transaction.getMedia(file.fileId) != nil {
|
||||
|
@ -249,7 +249,7 @@ private func pushDeviceContacts(postbox: Postbox, network: Network, importableCo
|
||||
if let updatedData = importableContacts[number] {
|
||||
if let value = value as? TelegramDeviceContactImportedData {
|
||||
switch value {
|
||||
case let .imported(data, _):
|
||||
case let .imported(data, _, _):
|
||||
if data != updatedData {
|
||||
updatedDataIdentifiers.insert(identifier)
|
||||
}
|
||||
@ -289,7 +289,10 @@ private func pushDeviceContacts(postbox: Postbox, network: Network, importableCo
|
||||
outer: for i in (0 ..< orderedPushIdentifiers.count).reversed() {
|
||||
if let user = currentContactDetails[orderedPushIdentifiers[i]], case let .phoneNumber(number) = orderedPushIdentifiers[i], let data = importableContacts[number] {
|
||||
if (user.firstName ?? "") == data.firstName && (user.lastName ?? "") == data.lastName {
|
||||
transaction.setDeviceContactImportInfo(orderedPushIdentifiers[i].key, value: TelegramDeviceContactImportedData.imported(data: data, importedByCount: 0))
|
||||
if data.localIdentifiers.contains("5DFF1D6F-8C0A-48C9-800D-F4BEC59C0E50") {
|
||||
assert(true)
|
||||
}
|
||||
transaction.setDeviceContactImportInfo(orderedPushIdentifiers[i].key, value: TelegramDeviceContactImportedData.imported(data: data, importedByCount: 0, peerId: user.id))
|
||||
orderedPushIdentifiers.remove(at: i)
|
||||
continue outer
|
||||
}
|
||||
@ -334,6 +337,7 @@ private func pushDeviceContactData(postbox: Postbox, network: Network, contacts:
|
||||
var addedContactPeerIds = Set<PeerId>()
|
||||
var retryIndices = Set<Int>()
|
||||
var importedCounts: [Int: Int32] = [:]
|
||||
var peerIdByClientId: [Int64: PeerId] = [:]
|
||||
switch result {
|
||||
case let .importedContacts(imported, popularInvites, retryContacts, users):
|
||||
let peers = users.map { TelegramUser(user: $0) as Peer }
|
||||
@ -342,8 +346,10 @@ private func pushDeviceContactData(postbox: Postbox, network: Network, contacts:
|
||||
})
|
||||
for item in imported {
|
||||
switch item {
|
||||
case let .importedContact(userId, _):
|
||||
addedContactPeerIds.insert(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)))
|
||||
case let .importedContact(userId, clientId):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
addedContactPeerIds.insert(peerId)
|
||||
peerIdByClientId[clientId] = peerId
|
||||
}
|
||||
}
|
||||
for item in retryContacts {
|
||||
@ -363,7 +369,10 @@ private func pushDeviceContactData(postbox: Postbox, network: Network, contacts:
|
||||
importedData = .retryLater
|
||||
addedReimportAttempts[.phoneNumber(batch[i].0)] = timestamp
|
||||
} else {
|
||||
importedData = .imported(data: batch[i].1, importedByCount: importedCounts[i] ?? 0)
|
||||
if batch[i].1.localIdentifiers.contains("5DFF1D6F-8C0A-48C9-800D-F4BEC59C0E50") {
|
||||
assert(true)
|
||||
}
|
||||
importedData = .imported(data: batch[i].1, importedByCount: importedCounts[i] ?? 0, peerId: peerIdByClientId[Int64(i)])
|
||||
}
|
||||
transaction.setDeviceContactImportInfo(TelegramDeviceContactImportIdentifier.phoneNumber(batch[i].0).key, value: importedData)
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo)
|
||||
switch result {
|
||||
case .stickerSetNotModified:
|
||||
break
|
||||
case let .stickerSet(stickerSet, packs, documents):
|
||||
case let .stickerSet(stickerSet, packs, keywords, documents):
|
||||
updatedInfo = StickerPackCollectionInfo(apiSet: stickerSet, namespace: info.id.namespace)
|
||||
var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:]
|
||||
for pack in packs {
|
||||
@ -165,6 +165,20 @@ private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo)
|
||||
break
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for apiDocument in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
|
||||
@ -224,7 +238,7 @@ private func installRemoteStickerPacks(network: Network, infos: [StickerPackColl
|
||||
archivedIds.insert(StickerPackCollectionInfo(apiSet: set, namespace: info.id.namespace).id)
|
||||
case let .stickerSetMultiCovered(set, _):
|
||||
archivedIds.insert(StickerPackCollectionInfo(apiSet: set, namespace: info.id.namespace).id)
|
||||
case let .stickerSetFullCovered(set, _, _):
|
||||
case let .stickerSetFullCovered(set, _, _, _):
|
||||
archivedIds.insert(StickerPackCollectionInfo(apiSet: set, namespace: info.id.namespace).id)
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 146
|
||||
return 147
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -227,7 +227,7 @@ func parsePreviewStickerSet(_ set: Api.StickerSetCovered, namespace: ItemCollect
|
||||
}
|
||||
}
|
||||
return (info, items)
|
||||
case let .stickerSetFullCovered(set, packs, documents):
|
||||
case let .stickerSetFullCovered(set, packs, keywords, documents):
|
||||
var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:]
|
||||
for pack in packs {
|
||||
switch pack {
|
||||
@ -244,6 +244,20 @@ func parsePreviewStickerSet(_ set: Api.StickerSetCovered, namespace: ItemCollect
|
||||
break
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let info = StickerPackCollectionInfo(apiSet: set, namespace: namespace)
|
||||
var items: [StickerPackItem] = []
|
||||
|
@ -74,7 +74,7 @@ public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMe
|
||||
switch result {
|
||||
case .stickerSetNotModified:
|
||||
break
|
||||
case let .stickerSet(_, packs, _):
|
||||
case let .stickerSet(_, packs, _, _):
|
||||
var stringRepresentationsByFile: [MediaId: [String]] = [:]
|
||||
for pack in packs {
|
||||
switch pack {
|
||||
|
@ -3,20 +3,24 @@ import Postbox
|
||||
public final class ImportableDeviceContactData: Equatable, PostboxCoding {
|
||||
public let firstName: String
|
||||
public let lastName: String
|
||||
public let localIdentifiers: [String]
|
||||
|
||||
public init(firstName: String, lastName: String) {
|
||||
public init(firstName: String, lastName: String, localIdentifiers: [String]) {
|
||||
self.firstName = firstName
|
||||
self.lastName = lastName
|
||||
self.localIdentifiers = localIdentifiers
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.firstName = decoder.decodeStringForKey("f", orElse: "")
|
||||
self.lastName = decoder.decodeStringForKey("l", orElse: "")
|
||||
self.localIdentifiers = decoder.decodeStringArrayForKey("dis")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeString(self.firstName, forKey: "f")
|
||||
encoder.encodeString(self.lastName, forKey: "l")
|
||||
encoder.encodeStringArray(self.localIdentifiers, forKey: "dis")
|
||||
}
|
||||
|
||||
public static func ==(lhs: ImportableDeviceContactData, rhs: ImportableDeviceContactData) -> Bool {
|
||||
@ -26,6 +30,9 @@ public final class ImportableDeviceContactData: Equatable, PostboxCoding {
|
||||
if lhs.lastName != rhs.lastName {
|
||||
return false
|
||||
}
|
||||
if lhs.localIdentifiers != rhs.localIdentifiers {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import Postbox
|
||||
|
||||
public enum TelegramDeviceContactImportedData: PostboxCoding {
|
||||
case imported(data: ImportableDeviceContactData, importedByCount: Int32)
|
||||
case imported(data: ImportableDeviceContactData, importedByCount: Int32, peerId: PeerId?)
|
||||
case retryLater
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_t", orElse: 0) {
|
||||
case 0:
|
||||
self = .imported(data: decoder.decodeObjectForKey("d", decoder: { ImportableDeviceContactData(decoder: $0) }) as! ImportableDeviceContactData, importedByCount: decoder.decodeInt32ForKey("c", orElse: 0))
|
||||
self = .imported(data: decoder.decodeObjectForKey("d", decoder: { ImportableDeviceContactData(decoder: $0) }) as! ImportableDeviceContactData, importedByCount: decoder.decodeInt32ForKey("c", orElse: 0), peerId: decoder.decodeOptionalInt64ForKey("pid").flatMap(PeerId.init))
|
||||
case 1:
|
||||
self = .retryLater
|
||||
default:
|
||||
@ -18,10 +18,15 @@ public enum TelegramDeviceContactImportedData: PostboxCoding {
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .imported(data, importedByCount):
|
||||
case let .imported(data, importedByCount, peerId):
|
||||
encoder.encodeInt32(0, forKey: "_t")
|
||||
encoder.encodeObject(data, forKey: "d")
|
||||
encoder.encodeInt32(importedByCount, forKey: "c")
|
||||
if let peerId = peerId {
|
||||
encoder.encodeInt64(peerId.toInt64(), forKey: "pid")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "pid")
|
||||
}
|
||||
case .retryLater:
|
||||
encoder.encodeInt32(1, forKey: "_t")
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func _internal_deviceContactsImportedByCount(postbox: Postbox, contacts: [(Strin
|
||||
for (id, numbers) in contacts {
|
||||
var maxCount: Int32 = 0
|
||||
for number in numbers {
|
||||
if let value = transaction.getDeviceContactImportInfo(TelegramDeviceContactImportIdentifier.phoneNumber(number).key) as? TelegramDeviceContactImportedData, case let .imported(_, importedByCount) = value {
|
||||
if let value = transaction.getDeviceContactImportInfo(TelegramDeviceContactImportIdentifier.phoneNumber(number).key) as? TelegramDeviceContactImportedData, case let .imported(_, importedByCount, _) = value {
|
||||
maxCount = max(maxCount, importedByCount)
|
||||
}
|
||||
}
|
||||
|
@ -69,5 +69,32 @@ public extension TelegramEngine {
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func findPeerByLocalContactIdentifier(identifier: String) -> Signal<EnginePeer?, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> EnginePeer? in
|
||||
var foundPeerId: PeerId?
|
||||
transaction.enumerateDeviceContactImportInfoItems({ _, value in
|
||||
if let value = value as? TelegramDeviceContactImportedData {
|
||||
switch value {
|
||||
case let .imported(data, _, peerId):
|
||||
if data.localIdentifiers.contains(identifier) {
|
||||
if let peerId = peerId {
|
||||
foundPeerId = peerId
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if let foundPeerId = foundPeerId {
|
||||
return transaction.getPeer(foundPeerId).flatMap(EnginePeer.init)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri
|
||||
switch result {
|
||||
case .stickerSetNotModified:
|
||||
return .complete()
|
||||
case let .stickerSet(set, packs, documents):
|
||||
case let .stickerSet(set, packs, keywords, documents):
|
||||
let namespace: ItemCollectionId.Namespace
|
||||
switch set {
|
||||
case let .stickerSet(flags, _, _, _, _, _, _, _, _, _, _, _):
|
||||
@ -195,6 +195,20 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri
|
||||
}
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for apiDocument in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
|
||||
|
@ -52,7 +52,7 @@ func updatedRemoteStickerPack(postbox: Postbox, network: Network, reference: Sti
|
||||
switch result {
|
||||
case .stickerSetNotModified:
|
||||
return .complete()
|
||||
case let .stickerSet(set, packs, documents):
|
||||
case let .stickerSet(set, packs, keywords, documents):
|
||||
let namespace: ItemCollectionId.Namespace
|
||||
switch set {
|
||||
case let .stickerSet(flags, _, _, _, _, _, _, _, _, _, _, _):
|
||||
@ -80,6 +80,20 @@ func updatedRemoteStickerPack(postbox: Postbox, network: Network, reference: Sti
|
||||
}
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for apiDocument in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
|
||||
|
@ -103,7 +103,7 @@ func _internal_stickerPacksAttachedToMedia(account: Account, media: AnyMediaRefe
|
||||
|> map { result -> [StickerPackReference] in
|
||||
return result.map { pack in
|
||||
switch pack {
|
||||
case let .stickerSetCovered(set, _), let .stickerSetMultiCovered(set, _), let .stickerSetFullCovered(set, _, _):
|
||||
case let .stickerSetCovered(set, _), let .stickerSetMultiCovered(set, _), let .stickerSetFullCovered(set, _, _, _):
|
||||
let info = StickerPackCollectionInfo(apiSet: set, namespace: Namespaces.ItemCollection.CloudStickerPacks)
|
||||
return .id(id: info.id.id, accessHash: info.accessHash)
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func _internal_requestStickerSet(postbox: Postbox, network: Network, reference:
|
||||
switch result {
|
||||
case .stickerSetNotModified:
|
||||
return .complete()
|
||||
case let .stickerSet(set, packs, documents):
|
||||
case let .stickerSet(set, packs, keywords, documents):
|
||||
info = StickerPackCollectionInfo(apiSet: set, namespace: Namespaces.ItemCollection.CloudStickerPacks)
|
||||
|
||||
switch set {
|
||||
@ -95,6 +95,20 @@ func _internal_requestStickerSet(postbox: Postbox, network: Network, reference:
|
||||
break
|
||||
}
|
||||
}
|
||||
for keyword in keywords {
|
||||
switch keyword {
|
||||
case let .stickerKeyword(documentId, texts):
|
||||
for text in texts {
|
||||
let key = ValueBoxKey(text).toMemoryBuffer()
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: documentId)
|
||||
if indexKeysByFile[mediaId] == nil {
|
||||
indexKeysByFile[mediaId] = [key]
|
||||
} else {
|
||||
indexKeysByFile[mediaId]!.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for apiDocument in documents {
|
||||
if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
|
||||
@ -167,7 +181,7 @@ func _internal_installStickerSetInteractively(account: Account, info: StickerPac
|
||||
case let .stickerSetMultiCovered(set: set, covers: covers):
|
||||
apiSet = set
|
||||
apiDocuments = covers
|
||||
case let .stickerSetFullCovered(set, _, documents):
|
||||
case let .stickerSetFullCovered(set, _, _, documents):
|
||||
apiSet = set
|
||||
apiDocuments = documents
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import DebugSettingsUI
|
||||
import BackgroundTasks
|
||||
import UIKitRuntimeUtils
|
||||
import StoreKit
|
||||
import PhoneNumberFormat
|
||||
|
||||
#if canImport(AppCenter)
|
||||
import AppCenter
|
||||
@ -1557,11 +1558,14 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let phoneNumber = payloadJson["phoneNumber"] as? String
|
||||
|
||||
callKitIntegration.reportIncomingCall(
|
||||
uuid: CallSessionManager.getStableIncomingUUID(stableId: callUpdate.callId),
|
||||
stableId: callUpdate.callId,
|
||||
handle: "\(callUpdate.peer.id.id._internalGetInt64Value())",
|
||||
phoneNumber: phoneNumber.flatMap(formatPhoneNumber),
|
||||
isVideo: false,
|
||||
displayTitle: callUpdate.peer.debugDisplayTitle,
|
||||
completion: { error in
|
||||
@ -1745,52 +1749,65 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
|
||||
if let contact = startCallContacts.first {
|
||||
var processed = false
|
||||
if let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
||||
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
|
||||
if let userId = Int64(string) {
|
||||
startCall(userId)
|
||||
processed = true
|
||||
}
|
||||
let contactByIdentifier: Signal<EnginePeer?, NoError>
|
||||
if let context = self.contextValue?.context, let contactIdentifier = contact.contactIdentifier {
|
||||
contactByIdentifier = context.engine.contacts.findPeerByLocalContactIdentifier(identifier: contactIdentifier)
|
||||
} else {
|
||||
contactByIdentifier = .single(nil)
|
||||
}
|
||||
if !processed, let handle = contact.personHandle, let value = handle.value {
|
||||
switch handle.type {
|
||||
case .unknown:
|
||||
if let userId = Int64(value) {
|
||||
startCall(userId)
|
||||
processed = true
|
||||
}
|
||||
case .phoneNumber:
|
||||
let phoneNumber = cleanPhoneNumber(value)
|
||||
if !phoneNumber.isEmpty {
|
||||
guard let context = self.contextValue?.context else {
|
||||
return true
|
||||
|
||||
let _ = (contactByIdentifier |> deliverOnMainQueue).start(next: { peerByContact in
|
||||
var processed = false
|
||||
if let peerByContact = peerByContact {
|
||||
startCall(peerByContact.id.id._internalGetInt64Value())
|
||||
processed = true
|
||||
} else if let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
||||
let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2))
|
||||
if let userId = Int64(string) {
|
||||
startCall(userId)
|
||||
processed = true
|
||||
}
|
||||
}
|
||||
if !processed, let handle = contact.personHandle, let value = handle.value {
|
||||
switch handle.type {
|
||||
case .unknown:
|
||||
if let userId = Int64(value) {
|
||||
startCall(userId)
|
||||
processed = true
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.List(includePresences: false))
|
||||
|> map { contactList -> PeerId? in
|
||||
var result: PeerId?
|
||||
for peer in contactList.peers {
|
||||
if case let .user(peer) = peer, let peerPhoneNumber = peer.phone {
|
||||
if matchPhoneNumbers(phoneNumber, peerPhoneNumber) {
|
||||
result = peer.id
|
||||
break
|
||||
case .phoneNumber:
|
||||
let phoneNumber = cleanPhoneNumber(value)
|
||||
if !phoneNumber.isEmpty {
|
||||
guard let context = self.contextValue?.context else {
|
||||
return
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.List(includePresences: false))
|
||||
|> map { contactList -> PeerId? in
|
||||
var result: PeerId?
|
||||
for peer in contactList.peers {
|
||||
if case let .user(peer) = peer, let peerPhoneNumber = peer.phone {
|
||||
if matchPhoneNumbers(phoneNumber, peerPhoneNumber) {
|
||||
result = peer.id
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result
|
||||
|> deliverOnMainQueue).start(next: { peerId in
|
||||
if let peerId = peerId {
|
||||
startCall(peerId.id._internalGetInt64Value())
|
||||
}
|
||||
})
|
||||
processed = true
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peerId in
|
||||
if let peerId = peerId {
|
||||
startCall(peerId.id._internalGetInt64Value())
|
||||
}
|
||||
})
|
||||
processed = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
} else if let sendMessageIntent = userActivity.interaction?.intent as? INSendMessageIntent {
|
||||
if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") {
|
||||
|
@ -1496,8 +1496,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if packReferences.count > 1 {
|
||||
items.tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(packReferences.count)), arguments: nil, file: nil, action: action)
|
||||
} else if let reference = packReferences.first {
|
||||
items.tipSignal = context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false)
|
||||
|> delay(1.0, queue: .mainQueue())
|
||||
var tipSignal: Signal<LoadedStickerPack, NoError>
|
||||
tipSignal = context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false)
|
||||
|
||||
items.tipSignal = tipSignal
|
||||
|
||||
|> filter { result in
|
||||
if case .result = result {
|
||||
return true
|
||||
|
@ -499,7 +499,10 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
var emojiStatus: PeerEmojiStatus?
|
||||
if let user = interfaceState.renderedPeer?.peer as? TelegramUser, let emojiStatusValue = user.emojiStatus {
|
||||
emojiStatus = emojiStatusValue
|
||||
if user.isFake || user.isScam {
|
||||
} else {
|
||||
emojiStatus = emojiStatusValue
|
||||
}
|
||||
}
|
||||
|
||||
/*#if DEBUG
|
||||
|
@ -150,12 +150,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
if peer.id != self.context.account.peerId {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
titleCredibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isFake {
|
||||
if peer.isFake {
|
||||
titleCredibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
titleCredibilityIcon = .scam
|
||||
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
titleCredibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isVerified {
|
||||
titleCredibilityIcon = .verified
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
|
@ -429,15 +429,20 @@ private final class DeviceContactDataManagerPrivateImpl {
|
||||
for (stableId, basicData) in self.stableIdToBasicContactData {
|
||||
for phoneNumber in basicData.phoneNumbers {
|
||||
var replace = false
|
||||
var currentLocalIdentifiers: [String] = []
|
||||
if let current = importableContactData[phoneNumber.value] {
|
||||
if stableId < current.0 {
|
||||
replace = true
|
||||
currentLocalIdentifiers = current.1.localIdentifiers
|
||||
}
|
||||
} else {
|
||||
replace = true
|
||||
}
|
||||
if replace {
|
||||
importableContactData[phoneNumber.value] = (stableId, ImportableDeviceContactData(firstName: basicData.firstName, lastName: basicData.lastName))
|
||||
if !currentLocalIdentifiers.contains(stableId) {
|
||||
currentLocalIdentifiers.append(stableId)
|
||||
}
|
||||
importableContactData[phoneNumber.value] = (stableId, ImportableDeviceContactData(firstName: basicData.firstName, lastName: basicData.lastName, localIdentifiers: currentLocalIdentifiers))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2311,13 +2311,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let credibilityIcon: CredibilityIcon
|
||||
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
credibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if let peer = peer {
|
||||
if let peer = peer {
|
||||
if peer.isFake {
|
||||
credibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
credibilityIcon = .scam
|
||||
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
credibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isVerified {
|
||||
credibilityIcon = .verified
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#import "NSWeakReference.h"
|
||||
|
||||
|
||||
@interface UIViewControllerPresentingProxy : UIViewController
|
||||
|
||||
@property (nonatomic, copy) void (^dismiss)();
|
||||
@ -139,6 +140,18 @@ static bool notyfyingShiftState = false;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIWindow (Telegram)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIWindow (Telegram)
|
||||
|
||||
- (instancetype)_65087dc8_initWithFrame:(CGRect)frame {
|
||||
return [self _65087dc8_initWithFrame:frame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@protocol UIRemoteKeyboardWindowProtocol
|
||||
|
||||
+ (UIWindow * _Nullable)remoteKeyboardWindowForScreen:(UIScreen * _Nullable)screen create:(BOOL)create;
|
||||
@ -161,7 +174,7 @@ static bool notyfyingShiftState = false;
|
||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentViewController:animated:completion:) newSelector:@selector(_65087dc8_presentViewController:animated:completion:)];
|
||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(setNeedsStatusBarAppearanceUpdate) newSelector:@selector(_65087dc8_setNeedsStatusBarAppearanceUpdate)];
|
||||
|
||||
[RuntimeUtils swizzleClassMethodOfClass:NSClassFromString(@"UIRemoteKeyboardWindow") currentSelector:NSSelectorFromString(@"remoteKeyboardWindowForScreen:create:") newSelector:NSSelectorFromString(@"_65087dc8_remoteKeyboardWindowForScreen:create:")];
|
||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIWindow class] currentSelector:@selector(initWithFrame:) newSelector:@selector(_65087dc8_initWithFrame:)];
|
||||
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[RuntimeUtils swizzleInstanceMethodOfClass:[CADisplayLink class] currentSelector:@selector(setPreferredFrameRateRange:) newSelector:@selector(_65087dc8_setPreferredFrameRateRange:)];
|
||||
@ -304,7 +317,8 @@ static bool notyfyingShiftState = false;
|
||||
if (!windowClass) {
|
||||
return nil;
|
||||
}
|
||||
return [(id<UIRemoteKeyboardWindowProtocol>)windowClass remoteKeyboardWindowForScreen:[UIScreen mainScreen] create:false];
|
||||
UIWindow *result = [(id<UIRemoteKeyboardWindowProtocol>)windowClass remoteKeyboardWindowForScreen:[UIScreen mainScreen] create:false];
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
x
Reference in New Issue
Block a user