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

This commit is contained in:
Ilya Laktyushin 2021-10-27 22:03:09 +04:00
commit f1e5016f46
12 changed files with 445 additions and 281 deletions

View File

@ -166,7 +166,7 @@ public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable {
let offsetIdHighBits = (data >> (32 + 3)) & 0xffffffff
let idHighBits = offsetIdHighBits << 32
if idHighBits == 0 {
if idHighBits == 0 && namespaceBits != 0 {
if let uint32Value = UInt32(exactly: idLowBits) {
self.id = Id(rawValue: Int64(Int32(bitPattern: uint32Value)))
} else {

View File

@ -28,8 +28,28 @@ public final class CallKitIntegration {
var audioSessionActive: Signal<Bool, NoError> {
return self.audioSessionActivePromise.get()
}
private static let sharedInstance: CallKitIntegration? = CallKitIntegration()
public static var shared: CallKitIntegration? {
return self.sharedInstance
}
func setup(
startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>,
answerCall: @escaping (UUID) -> Void,
endCall: @escaping (UUID) -> Signal<Bool, NoError>,
setCallMuted: @escaping (UUID, Bool) -> Void,
audioSessionActivationChanged: @escaping (Bool) -> Void
) {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if sharedProviderDelegate == nil {
sharedProviderDelegate = CallKitProviderDelegate()
}
(sharedProviderDelegate as? CallKitProviderDelegate)?.setup(audioSessionActivePromise: self.audioSessionActivePromise, startCall: startCall, answerCall: answerCall, endCall: endCall, setCallMuted: setCallMuted, audioSessionActivationChanged: audioSessionActivationChanged)
}
}
init?(startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
private init?() {
if !CallKitIntegration.isAvailable {
return nil
}
@ -39,10 +59,6 @@ public final class CallKitIntegration {
#else
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if sharedProviderDelegate == nil {
sharedProviderDelegate = CallKitProviderDelegate()
}
(sharedProviderDelegate as? CallKitProviderDelegate)?.setup(audioSessionActivePromise: self.audioSessionActivePromise, startCall: startCall, answerCall: answerCall, endCall: endCall, setCallMuted: setCallMuted, audioSessionActivationChanged: audioSessionActivationChanged)
} else {
return nil
}
@ -68,9 +84,9 @@ public final class CallKitIntegration {
}
}
func reportIncomingCall(uuid: UUID, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
public func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
(sharedProviderDelegate as? CallKitProviderDelegate)?.reportIncomingCall(uuid: uuid, handle: handle, isVideo: isVideo, displayTitle: displayTitle, completion: completion)
(sharedProviderDelegate as? CallKitProviderDelegate)?.reportIncomingCall(uuid: uuid, stableId: stableId, handle: handle, isVideo: isVideo, displayTitle: displayTitle, completion: completion)
}
}
@ -101,6 +117,8 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
private let callController = CXCallController()
private var currentStartCallAccount: (UUID, AccountContext)?
private var alreadyReportedIncomingCalls = Set<UUID>()
private var startCall: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
private var answerCall: ((UUID) -> Void)?
@ -163,7 +181,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
}
func answerCall(uuid: UUID) {
let answerCallAction = CXAnswerCallAction(call: uuid)
let transaction = CXTransaction(action: answerCallAction)
self.requestTransaction(transaction)
}
func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
@ -189,7 +209,13 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
})
}
func reportIncomingCall(uuid: UUID, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
func reportIncomingCall(uuid: UUID, stableId: Int64, handle: String, isVideo: Bool, displayTitle: String, completion: ((NSError?) -> Void)?) {
if self.alreadyReportedIncomingCalls.contains(uuid) {
completion?(nil)
return
}
self.alreadyReportedIncomingCalls.insert(uuid)
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: handle)
update.localizedCallerName = displayTitle

View File

@ -608,27 +608,34 @@ public final class PresentationCallImpl: PresentationCall {
case .ringing:
presentationState = PresentationCallState(state: .ringing, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
if previous == nil || previousControl == nil {
if !self.reportedIncomingCall {
if !self.reportedIncomingCall, let stableId = sessionState.stableId {
self.reportedIncomingCall = true
self.callKitIntegration?.reportIncomingCall(uuid: self.internalId, handle: "\(self.peerId.id)", isVideo: sessionState.type == .video, displayTitle: self.peer?.debugDisplayTitle ?? "Unknown", completion: { [weak self] error in
if let error = error {
if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) {
Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode")
Queue.mainQueue().async {
/*if let strongSelf = self {
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .busy, debugLog: .single(nil))
}*/
}
} else {
Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)")
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .hangUp, debugLog: .single(nil))
self.callKitIntegration?.reportIncomingCall(
uuid: self.internalId,
stableId: stableId,
handle: "\(self.peerId.id)",
isVideo: sessionState.type == .video,
displayTitle: self.peer?.debugDisplayTitle ?? "Unknown",
completion: { [weak self] error in
if let error = error {
if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) {
Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode")
Queue.mainQueue().async {
/*if let strongSelf = self {
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .busy, debugLog: .single(nil))
}*/
}
} else {
Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)")
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .hangUp, debugLog: .single(nil))
}
}
}
}
}
})
)
}
}
case .accepting:

View File

@ -113,7 +113,14 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
}
public init(accountManager: AccountManager<TelegramAccountManagerTypes>, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[AccountContext], NoError>) {
public init(
accountManager: AccountManager<TelegramAccountManagerTypes>,
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
isMediaPlaying: @escaping () -> Bool,
resumeMediaPlayback: @escaping () -> Void,
audioSession: ManagedAudioSession,
activeAccounts: Signal<[AccountContext], NoError>
) {
self.getDeviceAccessData = getDeviceAccessData
self.accountManager = accountManager
self.audioSession = audioSession
@ -127,7 +134,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
var setCallMutedImpl: ((UUID, Bool) -> Void)?
var audioSessionActivationChangedImpl: ((Bool) -> Void)?
self.callKitIntegration = CallKitIntegration(startCall: { context, uuid, handle, isVideo in
self.callKitIntegration = CallKitIntegration.shared
self.callKitIntegration?.setup(startCall: { context, uuid, handle, isVideo in
if let startCallImpl = startCallImpl {
return startCallImpl(context, uuid, handle, isVideo)
} else {

View File

@ -675,6 +675,11 @@ public struct AccountRunningImportantTasks: OptionSet {
public struct MasterNotificationKey: Codable {
public let id: Data
public let data: Data
public init(id: Data, data: Data) {
self.id = id
self.data = data
}
}
public func masterNotificationsKey(account: Account, ignoreDisabled: Bool) -> Signal<MasterNotificationKey, NoError> {
@ -810,7 +815,8 @@ public func accountBackupData(postbox: Postbox) -> Signal<AccountBackupData?, No
peerId: state.peerId.toInt64(),
masterDatacenterKey: authKey,
masterDatacenterKeyId: datacenterAuthInfo.authKeyId,
notificationEncryptionKeyId: notificationsKey?.id
notificationEncryptionKeyId: notificationsKey?.id,
notificationEncryptionKey: notificationsKey?.data
)
}
}

View File

@ -135,90 +135,95 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
deinit {
assert(self.queue.isCurrent())
}
fileprivate func transactionSync<T>(ignoreDisabled: Bool, _ f: (AccountManagerModifier<Types>) -> T) -> T {
self.valueBox.begin()
let transaction = AccountManagerModifier<Types>(getRecords: {
return self.currentAtomicState.records.map { $0.1 }
}, updateRecord: { id, update in
let current = self.currentAtomicState.records[id]
let updated = update(current)
if updated != current {
if let updated = updated {
self.currentAtomicState.records[id] = updated
} else {
self.currentAtomicState.records.removeValue(forKey: id)
}
self.currentAtomicStateUpdated = true
self.currentRecordOperations.append(.set(id: id, record: updated))
}
}, getCurrent: {
if let id = self.currentAtomicState.currentRecordId, let record = self.currentAtomicState.records[id] {
return (record.id, record.attributes)
} else {
return nil
}
}, setCurrentId: { id in
self.currentAtomicState.currentRecordId = id
self.currentMetadataOperations.append(.updateCurrentAccountId(id))
self.currentAtomicStateUpdated = true
}, getCurrentAuth: {
if let record = self.currentAtomicState.currentAuthRecord {
return record
} else {
return nil
}
}, createAuth: { attributes in
let record = AuthAccountRecord<Types.Attribute>(id: generateAccountRecordId(), attributes: attributes)
self.currentAtomicState.currentAuthRecord = record
self.currentAtomicStateUpdated = true
self.currentMetadataOperations.append(.updateCurrentAuthAccountRecord(record))
return record
}, removeAuth: {
self.currentAtomicState.currentAuthRecord = nil
self.currentMetadataOperations.append(.updateCurrentAuthAccountRecord(nil))
self.currentAtomicStateUpdated = true
}, createRecord: { attributes in
let id = generateAccountRecordId()
let record = AccountRecord<Types.Attribute>(id: id, attributes: attributes, temporarySessionId: nil)
self.currentAtomicState.records[id] = record
self.currentRecordOperations.append(.set(id: id, record: record))
self.currentAtomicStateUpdated = true
return id
}, getSharedData: { key in
return self.sharedDataTable.get(key: key)
}, updateSharedData: { key, f in
let updated = f(self.sharedDataTable.get(key: key))
self.sharedDataTable.set(key: key, value: updated, updatedKeys: &self.currentUpdatedSharedDataKeys)
}, getAccessChallengeData: {
return self.legacyMetadataTable.getAccessChallengeData()
}, setAccessChallengeData: { data in
self.currentUpdatedAccessChallengeData = data
self.currentAtomicStateUpdated = true
self.legacyMetadataTable.setAccessChallengeData(data)
self.currentAtomicState.accessChallengeData = data
}, getVersion: {
return self.legacyMetadataTable.getVersion()
}, setVersion: { version in
self.legacyMetadataTable.setVersion(version)
}, getNotice: { key in
self.noticeTable.get(key: key)
}, setNotice: { key, value in
self.noticeTable.set(key: key, value: value)
self.currentUpdatedNoticeEntryKeys.insert(key)
}, clearNotices: {
self.noticeTable.clear()
})
let result = f(transaction)
self.beforeCommit()
self.valueBox.commit()
return result
}
fileprivate func transaction<T>(ignoreDisabled: Bool, _ f: @escaping (AccountManagerModifier<Types>) -> T) -> Signal<T, NoError> {
return Signal { subscriber in
self.queue.justDispatch {
self.valueBox.begin()
let transaction = AccountManagerModifier<Types>(getRecords: {
return self.currentAtomicState.records.map { $0.1 }
}, updateRecord: { id, update in
let current = self.currentAtomicState.records[id]
let updated = update(current)
if updated != current {
if let updated = updated {
self.currentAtomicState.records[id] = updated
} else {
self.currentAtomicState.records.removeValue(forKey: id)
}
self.currentAtomicStateUpdated = true
self.currentRecordOperations.append(.set(id: id, record: updated))
}
}, getCurrent: {
if let id = self.currentAtomicState.currentRecordId, let record = self.currentAtomicState.records[id] {
return (record.id, record.attributes)
} else {
return nil
}
}, setCurrentId: { id in
self.currentAtomicState.currentRecordId = id
self.currentMetadataOperations.append(.updateCurrentAccountId(id))
self.currentAtomicStateUpdated = true
}, getCurrentAuth: {
if let record = self.currentAtomicState.currentAuthRecord {
return record
} else {
return nil
}
}, createAuth: { attributes in
let record = AuthAccountRecord<Types.Attribute>(id: generateAccountRecordId(), attributes: attributes)
self.currentAtomicState.currentAuthRecord = record
self.currentAtomicStateUpdated = true
self.currentMetadataOperations.append(.updateCurrentAuthAccountRecord(record))
return record
}, removeAuth: {
self.currentAtomicState.currentAuthRecord = nil
self.currentMetadataOperations.append(.updateCurrentAuthAccountRecord(nil))
self.currentAtomicStateUpdated = true
}, createRecord: { attributes in
let id = generateAccountRecordId()
let record = AccountRecord<Types.Attribute>(id: id, attributes: attributes, temporarySessionId: nil)
self.currentAtomicState.records[id] = record
self.currentRecordOperations.append(.set(id: id, record: record))
self.currentAtomicStateUpdated = true
return id
}, getSharedData: { key in
return self.sharedDataTable.get(key: key)
}, updateSharedData: { key, f in
let updated = f(self.sharedDataTable.get(key: key))
self.sharedDataTable.set(key: key, value: updated, updatedKeys: &self.currentUpdatedSharedDataKeys)
}, getAccessChallengeData: {
return self.legacyMetadataTable.getAccessChallengeData()
}, setAccessChallengeData: { data in
self.currentUpdatedAccessChallengeData = data
self.currentAtomicStateUpdated = true
self.legacyMetadataTable.setAccessChallengeData(data)
self.currentAtomicState.accessChallengeData = data
}, getVersion: {
return self.legacyMetadataTable.getVersion()
}, setVersion: { version in
self.legacyMetadataTable.setVersion(version)
}, getNotice: { key in
self.noticeTable.get(key: key)
}, setNotice: { key, value in
self.noticeTable.set(key: key, value: value)
self.currentUpdatedNoticeEntryKeys.insert(key)
}, clearNotices: {
self.noticeTable.clear()
})
let result = f(transaction)
self.beforeCommit()
self.valueBox.commit()
//self.valueBox.checkpoint()
let result = self.transactionSync(ignoreDisabled: ignoreDisabled, f)
subscriber.putNext(result)
subscriber.putCompletion()
@ -293,6 +298,13 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
})
|> switchToLatest
}
fileprivate func _internalAccountRecordsSync() -> AccountRecordsView<Types> {
let mutableView = MutableAccountRecordsView<Types>(getRecords: {
return self.currentAtomicState.records.map { $0.1 }
}, currentId: self.currentAtomicState.currentRecordId, currentAuth: self.currentAtomicState.currentAuthRecord)
return AccountRecordsView<Types>(mutableView)
}
fileprivate func sharedData(keys: Set<ValueBoxKey>) -> Signal<AccountSharedDataView<Types>, NoError> {
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountSharedDataView<Types>, NoError> in
@ -517,6 +529,14 @@ public final class AccountManager<Types: AccountManagerTypes> {
return disposable
}
}
public func _internalAccountRecordsSync() -> AccountRecordsView<Types> {
var result: AccountRecordsView<Types>?
self.impl.syncWith { impl in
result = impl._internalAccountRecordsSync()
}
return result!
}
public func sharedData(keys: Set<ValueBoxKey>) -> Signal<AccountSharedDataView<Types>, NoError> {
return Signal { subscriber in

View File

@ -1170,6 +1170,74 @@ public final class AccountStateManager {
func notifyDeletedMessages(messageIds: [MessageId]) {
self.deletedMessagesPipe.putNext(messageIds.map { .messageId($0) })
}
public final class IncomingCallUpdate {
public let callId: Int64
public let callAccessHash: Int64
public let timestamp: Int32
public let peer: EnginePeer
init(
callId: Int64,
callAccessHash: Int64,
timestamp: Int32,
peer: EnginePeer
) {
self.callId = callId
self.callAccessHash = callAccessHash
self.timestamp = timestamp
self.peer = peer
}
}
public static func extractIncomingCallUpdate(data: Data) -> IncomingCallUpdate? {
var rawData = data
let reader = BufferReader(Buffer(data: data))
if let signature = reader.readInt32(), signature == 0x3072cfa1 {
if let compressedData = parseBytes(reader) {
if let decompressedData = MTGzip.decompress(compressedData.makeData()) {
rawData = decompressedData
}
}
}
guard let updates = Api.parse(Buffer(data: rawData)) as? Api.Updates else {
return nil
}
switch updates {
case let .updates(updates, users, _, _, _):
var peers: [Peer] = []
for user in users {
peers.append(TelegramUser(user: user))
}
for update in updates {
switch update {
case let .updatePhoneCall(phoneCall):
switch phoneCall {
case let .phoneCallRequested(_, id, accessHash, date, adminId, _, _, _):
guard let peer = peers.first(where: { $0.id == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) }) else {
return nil
}
return IncomingCallUpdate(
callId: id,
callAccessHash: accessHash,
timestamp: date,
peer: EnginePeer(peer)
)
default:
break
}
default:
break
}
}
return nil
default:
return nil
}
}
public func processIncomingCallUpdate(data: Data, completion: @escaping ((CallSessionRingingState, CallSession)?) -> Void) {
var rawData = data

View File

@ -63,11 +63,53 @@ enum CallSessionInternalState {
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
var stableId: Int64? {
switch self {
case let .ringing(id, _, _, _, _):
return id
case let .accepting(id, _, _, _, _):
return id
case let .awaitingConfirmation(id, _, _, _, _):
return id
case .requesting:
return nil
case let .requested(id, _, _, _, _, _):
return id
case let .confirming(id, _, _, _, _, _):
return id
case let .active(id, _, _, _, _, _, _, _, _, _):
return id
case .dropping:
return nil
case let .terminated(id, _, _, _, _):
return id
}
}
}
public typealias CallSessionInternalId = UUID
typealias CallSessionStableId = Int64
private final class StableIncomingUUIDs {
static let shared = Atomic<StableIncomingUUIDs>(value: StableIncomingUUIDs())
private var dict: [Int64: UUID] = [:]
private init() {
}
func get(id: Int64) -> UUID {
if let value = self.dict[id] {
return value
} else {
let value = UUID()
self.dict[id] = value
return value
}
}
}
public struct CallSessionRingingState: Equatable {
public let id: CallSessionInternalId
public let peerId: PeerId
@ -147,10 +189,27 @@ public struct CallSession {
}
public let id: CallSessionInternalId
public let stableId: Int64?
public let isOutgoing: Bool
public let type: CallType
public let state: CallSessionState
public let isVideoPossible: Bool
init(
id: CallSessionInternalId,
stableId: Int64?,
isOutgoing: Bool,
type: CallType,
state: CallSessionState,
isVideoPossible: Bool
) {
self.id = id
self.stableId = stableId
self.isOutgoing = isOutgoing
self.type = type
self.state = state
self.isVideoPossible = isVideoPossible
}
}
public enum CallSessionConnection: Equatable {
@ -384,7 +443,7 @@ private final class CallSessionManagerContext {
let index = context.subscribers.add { next in
subscriber.putNext(next)
}
subscriber.putNext(CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible))
subscriber.putNext(CallSession(id: internalId, stableId: context.state.stableId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible))
disposable.set(ActionDisposable {
queue.async {
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
@ -447,7 +506,7 @@ private final class CallSessionManagerContext {
private func contextUpdated(internalId: CallSessionInternalId) {
if let context = self.contexts[internalId] {
let session = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
let session = CallSession(id: internalId, stableId: context.state.stableId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
for subscriber in context.subscribers.copyItems() {
subscriber(session)
}
@ -469,7 +528,7 @@ private final class CallSessionManagerContext {
isVideoPossible = true
//#endif
let internalId = CallSessionInternalId()
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))
self.contexts[internalId] = context
let queue = self.queue
@ -835,7 +894,7 @@ private final class CallSessionManagerContext {
}
}
if let context = self.contexts[internalId] {
let callSession = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
let callSession = CallSession(id: internalId, stableId: id, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
if let resultRingingStateValue = resultRingingStateValue {
resultRingingState = (resultRingingStateValue, callSession)
}
@ -948,6 +1007,12 @@ public enum CallRequestError {
}
public final class CallSessionManager {
public static func getStableIncomingUUID(stableId: Int64) -> UUID {
return StableIncomingUUIDs.shared.with { impl in
return impl.get(id: stableId)
}
}
private let queue = Queue()
private var contextRef: Unmanaged<CallSessionManagerContext>?

View File

@ -7,19 +7,22 @@ public struct AccountBackupData: Codable, Equatable {
public var masterDatacenterKey: Data
public var masterDatacenterKeyId: Int64
public var notificationEncryptionKeyId: Data?
public var notificationEncryptionKey: Data?
public init(
masterDatacenterId: Int32,
peerId: Int64,
masterDatacenterKey: Data,
masterDatacenterKeyId: Int64,
notificationEncryptionKeyId: Data?
notificationEncryptionKeyId: Data?,
notificationEncryptionKey: Data?
) {
self.masterDatacenterId = masterDatacenterId
self.peerId = peerId
self.masterDatacenterKey = masterDatacenterKey
self.masterDatacenterKeyId = masterDatacenterKeyId
self.notificationEncryptionKeyId = notificationEncryptionKeyId
self.notificationEncryptionKey = notificationEncryptionKey
}
}

View File

@ -175,6 +175,35 @@ final class SharedApplicationContext {
}
}
private struct AccountManagerState {
struct NotificationKey {
var accountId: AccountRecordId
var id: Data
var key: Data
}
var notificationKeys: [NotificationKey]
}
private func extractAccountManagerState(records: AccountRecordsView<TelegramAccountManagerTypes>) -> AccountManagerState {
return AccountManagerState(
notificationKeys: records.records.compactMap { record -> AccountManagerState.NotificationKey? in
for attribute in record.attributes {
if case let .backupData(backupData) = attribute {
if let notificationEncryptionKeyId = backupData.data?.notificationEncryptionKeyId, let notificationEncryptionKey = backupData.data?.notificationEncryptionKey {
return AccountManagerState.NotificationKey(
accountId: record.id,
id: notificationEncryptionKeyId,
key: notificationEncryptionKey
)
}
}
}
return nil
}
)
}
@objc(AppDelegate) class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate, UNUserNotificationCenterDelegate {
@objc var window: UIWindow?
var nativeWindow: (UIWindow & WindowHost)?
@ -192,6 +221,9 @@ final class SharedApplicationContext {
private let sharedContextPromise = Promise<SharedApplicationContext>()
private let watchCommunicationManagerPromise = Promise<WatchCommunicationManager?>()
private var accountManager: AccountManager<TelegramAccountManagerTypes>?
private var accountManagerState: AccountManagerState?
private var contextValue: AuthorizedApplicationContext?
private let context = Promise<AuthorizedApplicationContext?>()
@ -486,8 +518,6 @@ final class SharedApplicationContext {
UNUserNotificationCenter.current().delegate = self
}
telegramUIDeclareEncodables()
GlobalExperimentalSettings.isAppStoreBuild = buildConfig.isAppStoreBuild
GlobalExperimentalSettings.enableFeed = false
@ -495,8 +525,6 @@ final class SharedApplicationContext {
self.hasActiveAudioSession.set(MediaManagerImpl.globalAudioSession.isActive())
initializeAccountManagement()
let applicationBindings = TelegramApplicationBindings(isMainApp: true, appBundleId: baseAppBundleId, containerPath: appGroupUrl.path, appSpecificScheme: buildConfig.appSpecificUrlScheme, openUrl: { url in
var parsedUrl = URL(string: url)
if let parsed = parsedUrl {
@ -676,47 +704,29 @@ final class SharedApplicationContext {
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()
})
let accountManagerSignal = Signal<AccountManager<TelegramAccountManagerTypes>, NoError> { subscriber in
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true)
return (upgradedAccounts(accountManager: accountManager, rootPath: rootPath, encryptionParameters: encryptionParameters)
|> deliverOnMainQueue).start(next: { progress in
if self.dataImportSplash == nil {
self.dataImportSplash = makeLegacyDataImportSplash(theme: nil, strings: nil)
self.dataImportSplash?.serviceAction = {
self.debugPressed()
}
self.mainWindow.coveringView = self.dataImportSplash
}
self.dataImportSplash?.progress = (.generic, progress)
}, completed: {
if let dataImportSplash = self.dataImportSplash {
self.dataImportSplash = nil
if self.mainWindow.coveringView === dataImportSplash {
self.mainWindow.coveringView = nil
}
}
subscriber.putNext(accountManager)
subscriber.putCompletion()
})
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true)
self.accountManager = accountManager
telegramUIDeclareEncodables()
initializeAccountManagement()
self.accountManagerState = extractAccountManagerState(records: accountManager._internalAccountRecordsSync())
let _ = (accountManager.accountRecords()
|> deliverOnMainQueue).start(next: { view in
self.accountManagerState = extractAccountManagerState(records: view)
})
var systemUserInterfaceStyle: WindowUserInterfaceStyle = .light
if #available(iOS 13.0, *) {
if let traitCollection = window.rootViewController?.traitCollection {
systemUserInterfaceStyle = WindowUserInterfaceStyle(style: traitCollection.userInterfaceStyle)
}
}
let sharedContextSignal = accountManagerSignal
|> deliverOnMainQueue
|> take(1)
|> deliverOnMainQueue
|> take(1)
|> mapToSignal { accountManager -> Signal<(AccountManager<TelegramAccountManagerTypes>, InitialPresentationDataAndSettings), NoError> in
var systemUserInterfaceStyle: WindowUserInterfaceStyle = .light
if #available(iOS 13.0, *) {
if let traitCollection = window.rootViewController?.traitCollection {
systemUserInterfaceStyle = WindowUserInterfaceStyle(style: traitCollection.userInterfaceStyle)
}
}
return currentPresentationDataAndSettings(accountManager: accountManager, systemUserInterfaceStyle: systemUserInterfaceStyle)
|> map { initialPresentationDataAndSettings -> (AccountManager, InitialPresentationDataAndSettings) in
return (accountManager, initialPresentationDataAndSettings)
}
let sharedContextSignal = currentPresentationDataAndSettings(accountManager: accountManager, systemUserInterfaceStyle: systemUserInterfaceStyle)
|> map { initialPresentationDataAndSettings -> (AccountManager, InitialPresentationDataAndSettings) in
return (accountManager, initialPresentationDataAndSettings)
}
|> deliverOnMainQueue
|> mapToSignal { accountManager, initialPresentationDataAndSettings -> Signal<(SharedApplicationContext, LoggingSettings), NoError> in
@ -1417,7 +1427,7 @@ final class SharedApplicationContext {
}
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
/*guard var encryptedPayload = payload.dictionaryPayload["p"] as? String else {
guard var encryptedPayload = payload.dictionaryPayload["p"] as? String else {
return
}
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
@ -1425,11 +1435,81 @@ final class SharedApplicationContext {
while encryptedPayload.count % 4 != 0 {
encryptedPayload.append("=")
}
guard let data = Data(base64Encoded: encryptedPayload) else {
guard let payloadData = Data(base64Encoded: encryptedPayload) else {
return
}
guard let keyId = notificationPayloadKeyId(data: payloadData) else {
return
}
let semaphore = DispatchSemaphore(value: 0)
guard let accountManagerState = self.accountManagerState else {
return
}
var maybeAccountId: AccountRecordId?
var maybeNotificationKey: MasterNotificationKey?
for key in accountManagerState.notificationKeys {
if key.id == keyId {
maybeAccountId = key.accountId
maybeNotificationKey = MasterNotificationKey(id: key.id, data: key.key)
break
}
}
guard let accountId = maybeAccountId, let notificationKey = maybeNotificationKey else {
return
}
guard let decryptedPayload = decryptedNotificationPayload(key: notificationKey, data: payloadData) else {
return
}
guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else {
return
}
guard var updateString = payloadJson["updates"] as? String else {
return
}
updateString = updateString.replacingOccurrences(of: "-", with: "+")
updateString = updateString.replacingOccurrences(of: "_", with: "/")
while updateString.count % 4 != 0 {
updateString.append("=")
}
guard let updateData = Data(base64Encoded: updateString) else {
return
}
guard let callUpdate = AccountStateManager.extractIncomingCallUpdate(data: updateData) else {
return
}
guard let callKitIntegration = CallKitIntegration.shared else {
return
}
callKitIntegration.reportIncomingCall(
uuid: CallSessionManager.getStableIncomingUUID(stableId: callUpdate.callId),
stableId: callUpdate.callId,
handle: "\(callUpdate.peer.id.id)",
isVideo: false,
displayTitle: callUpdate.peer.debugDisplayTitle,
completion: { error in
if let error = error {
if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) {
Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode")
} else {
Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)")
/*Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .hangUp, debugLog: .single(nil))
}
}*/
}
}
}
)
let _ = accountId
/*let semaphore = DispatchSemaphore(value: 0)
var accountAndDecryptedPayload: (Account, Data)?
var sharedApplicationContextValue: SharedApplicationContext?
@ -1486,6 +1566,7 @@ final class SharedApplicationContext {
}
}
}*/
let _ = (self.sharedContextPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { sharedApplicationContext in

View File

@ -1507,7 +1507,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
guard let strongSelf = self else {
return
}
if count < 1 || true {
if count < 1 {
let _ = ApplicationSpecificNotice.incrementSharedMediaFastScrollingTooltip(accountManager: strongSelf.context.sharedContext.accountManager).start()
var currentNode: ASDisplayNode = strongSelf

View File

@ -1,121 +1 @@
import Foundation
import UIKit
import TelegramCore
import Postbox
import SwiftSignalKit
import TelegramUIPreferences
import MediaResources
private enum LegacyPreferencesKeyValues: Int32 {
case cacheStorageSettings = 1
case localizationSettings = 2
case proxySettings = 5
var key: ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: self.rawValue)
return key
}
}
private enum UpgradedSharedDataKeyValues: Int32 {
case cacheStorageSettings = 2
case localizationSettings = 3
case proxySettings = 4
var key: ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: self.rawValue)
return key
}
}
private enum LegacyApplicationSpecificPreferencesKeyValues: Int32 {
case inAppNotificationSettings = 0
case presentationPasscodeSettings = 1
case automaticMediaDownloadSettings = 2
case generatedMediaStoreSettings = 3
case voiceCallSettings = 4
case presentationThemeSettings = 5
case instantPagePresentationSettings = 6
case callListSettings = 7
case experimentalSettings = 8
case musicPlaybackSettings = 9
case mediaInputSettings = 10
case experimentalUISettings = 11
case contactSynchronizationSettings = 12
case stickerSettings = 13
case watchPresetSettings = 14
case webSearchSettings = 15
case voipDerivedState = 16
var key: ValueBoxKey {
return applicationSpecificPreferencesKey(self.rawValue)
}
}
private enum UpgradedApplicationSpecificSharedDataKeyValues: Int32 {
case inAppNotificationSettings = 0
case presentationPasscodeSettings = 1
case automaticMediaDownloadSettings = 2
case generatedMediaStoreSettings = 3
case voiceCallSettings = 4
case presentationThemeSettings = 5
case instantPagePresentationSettings = 6
case callListSettings = 7
case experimentalSettings = 8
case musicPlaybackSettings = 9
case mediaInputSettings = 10
case experimentalUISettings = 11
case stickerSettings = 12
case watchPresetSettings = 13
case webSearchSettings = 14
case contactSynchronizationSettings = 15
var key: ValueBoxKey {
return applicationSpecificSharedDataKey(self.rawValue)
}
}
private let preferencesKeyMapping: [LegacyPreferencesKeyValues: UpgradedSharedDataKeyValues] = [
.cacheStorageSettings: .cacheStorageSettings,
.localizationSettings: .localizationSettings,
.proxySettings: .proxySettings
]
private let applicationSpecificPreferencesKeyMapping: [LegacyApplicationSpecificPreferencesKeyValues: UpgradedApplicationSpecificSharedDataKeyValues] = [
.inAppNotificationSettings: .inAppNotificationSettings,
.presentationPasscodeSettings: .presentationPasscodeSettings,
.automaticMediaDownloadSettings: .automaticMediaDownloadSettings,
.generatedMediaStoreSettings: .generatedMediaStoreSettings,
.voiceCallSettings: .voiceCallSettings,
.presentationThemeSettings: .presentationThemeSettings,
.instantPagePresentationSettings: .instantPagePresentationSettings,
.callListSettings: .callListSettings,
.experimentalSettings: .experimentalSettings,
.musicPlaybackSettings: .musicPlaybackSettings,
.mediaInputSettings: .mediaInputSettings,
.experimentalUISettings: .experimentalUISettings,
.stickerSettings: .stickerSettings,
.watchPresetSettings: .watchPresetSettings,
.webSearchSettings: .webSearchSettings,
.contactSynchronizationSettings: .contactSynchronizationSettings
]
private func upgradedSharedDataValue(_ value: PreferencesEntry?) -> PreferencesEntry? {
return value
}
public func upgradedAccounts(accountManager: AccountManager<TelegramAccountManagerTypes>, rootPath: String, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<Float, NoError> {
return accountManager.transaction { transaction -> (Int32?, AccountRecordId?) in
return (transaction.getVersion(), transaction.getCurrent()?.0)
}
|> mapToSignal { version, currentId -> Signal<Float, NoError> in
return accountManager.transaction { transaction -> Void in
transaction.setVersion(4)
}
|> ignoreValues
|> mapToSignal { _ -> Signal<Float, NoError> in
}
}
}