This commit is contained in:
overtake 2017-04-18 19:56:08 +03:00
commit 5ca27728d7
21 changed files with 614 additions and 58 deletions

View File

@ -93,6 +93,10 @@
D01D6BFA1E42A718006151C6 /* SearchStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01D6BF81E42A713006151C6 /* SearchStickers.swift */; };
D021E0DF1DB539FC00C6B04F /* StickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0DE1DB539FC00C6B04F /* StickerPack.swift */; };
D021E0E21DB5401A00C6B04F /* StickerManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0E11DB5401A00C6B04F /* StickerManagement.swift */; };
D0223A981EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */; };
D0223A991EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */; };
D0223A9B1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */; };
D0223A9C1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */; };
D02ABC7B1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02ABC7A1E30058F00CAE539 /* DeleteMessagesInteractively.swift */; };
D02ABC7C1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02ABC7A1E30058F00CAE539 /* DeleteMessagesInteractively.swift */; };
D02ABC7E1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02ABC7D1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift */; };
@ -474,6 +478,10 @@
D0F7B1EB1E045C87007EB8A5 /* ResolvePeerByName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3CC7C1DDE289E008148FA /* ResolvePeerByName.swift */; };
D0F7B1EC1E045C87007EB8A5 /* SearchPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07827BA1E00451F00071108 /* SearchPeers.swift */; };
D0FA0ABD1E76C908005BB9B7 /* TwoStepVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA0ABC1E76C908005BB9B7 /* TwoStepVerification.swift */; };
D0FA35051EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */; };
D0FA35061EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */; };
D0FA35081EA632E400E56FFA /* CollectCacheUsageStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */; };
D0FA35091EA632E400E56FFA /* CollectCacheUsageStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */; };
D0FA8B981E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA8B971E1E955C001E855B /* SecretChatOutgoingOperation.swift */; };
D0FA8B991E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA8B971E1E955C001E855B /* SecretChatOutgoingOperation.swift */; };
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA8B9D1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift */; };
@ -549,6 +557,8 @@
D01D6BF81E42A713006151C6 /* SearchStickers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchStickers.swift; sourceTree = "<group>"; };
D021E0DE1DB539FC00C6B04F /* StickerPack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerPack.swift; sourceTree = "<group>"; };
D021E0E11DB5401A00C6B04F /* StickerManagement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerManagement.swift; sourceTree = "<group>"; };
D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResourceNetworkStatsTag.swift; sourceTree = "<group>"; };
D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramMediaResource.swift; sourceTree = "<group>"; };
D02ABC7A1E30058F00CAE539 /* DeleteMessagesInteractively.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeleteMessagesInteractively.swift; sourceTree = "<group>"; };
D02ABC7D1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudChatRemoveMessagesOperation.swift; sourceTree = "<group>"; };
D02ABC801E310E5D00CAE539 /* ManagedCloudChatRemoveMessagesOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedCloudChatRemoveMessagesOperations.swift; sourceTree = "<group>"; };
@ -767,6 +777,8 @@
D0F7AB2B1DCE889D009AD9A1 /* EditedMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditedMessageAttribute.swift; sourceTree = "<group>"; };
D0F7AB2E1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyMarkupMessageAttribute.swift; sourceTree = "<group>"; };
D0FA0ABC1E76C908005BB9B7 /* TwoStepVerification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwoStepVerification.swift; sourceTree = "<group>"; };
D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheStorageSettings.swift; sourceTree = "<group>"; };
D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectCacheUsageStats.swift; sourceTree = "<group>"; };
D0FA8B971E1E955C001E855B /* SecretChatOutgoingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatOutgoingOperation.swift; sourceTree = "<group>"; };
D0FA8B9D1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatIncomingEncryptedOperation.swift; sourceTree = "<group>"; };
D0FA8BA01E1F99E1001E855B /* SecretChatFileReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatFileReference.swift; sourceTree = "<group>"; };
@ -826,6 +838,7 @@
D01B27A11E394D8B0022A4C0 /* PrivacySettings.swift */,
D08774FD1E3E3A3500A97350 /* GlobalNotificationSettings.swift */,
D05A32E61E6F0B5C002760B4 /* RecentAccountSession.swift */,
D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */,
);
name = Settings;
sourceTree = "<group>";
@ -1057,7 +1070,9 @@
D03B0D121D62257600955575 /* Resources */ = {
isa = PBXGroup;
children = (
D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */,
D03B0D431D6319F900955575 /* CloudFileMediaResource.swift */,
D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */,
D03B0D391D6319E200955575 /* Fetch.swift */,
D0E35A0D1DE4953E00BC6096 /* FetchHttpResource.swift */,
D0448CA11E291B14005A61A7 /* FetchSecretFileResource.swift */,
@ -1130,6 +1145,7 @@
C239BE961E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift */,
D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */,
D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */,
D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */,
);
name = Messages;
sourceTree = "<group>";
@ -1628,6 +1644,7 @@
D019B1CC1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */,
D03B0CD91D62245B00955575 /* PeerUtils.swift in Sources */,
D03B0CE41D62249F00955575 /* TextEntitiesMessageAttribute.swift in Sources */,
D0FA35081EA632E400E56FFA /* CollectCacheUsageStats.swift in Sources */,
D049EAEB1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */,
D03B0CD31D62244300955575 /* Namespaces.swift in Sources */,
D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */,
@ -1649,6 +1666,7 @@
D0448C8E1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */,
D0448C911E251F96005A61A7 /* SecretChatEncryption.swift in Sources */,
D0FA8BA11E1F99E1001E855B /* SecretChatFileReference.swift in Sources */,
D0223A981EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */,
D07827BB1E00451F00071108 /* SearchPeers.swift in Sources */,
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
D0BC38771E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
@ -1667,6 +1685,7 @@
D03B0D0F1D62255C00955575 /* UpdateMessageService.swift in Sources */,
D03B0CF61D62250800955575 /* TelegramMediaFile.swift in Sources */,
D03B0CE81D6224AD00955575 /* ViewCountMessageAttribute.swift in Sources */,
D0FA35051EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */,
D03B0D0C1D62255C00955575 /* AccountStateManagementUtils.swift in Sources */,
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */,
@ -1682,6 +1701,7 @@
D03B0D5C1D631A6900955575 /* Download.swift in Sources */,
D01749591E1092BC0057C89A /* RequestStartBot.swift in Sources */,
D01B27A21E394D8B0022A4C0 /* PrivacySettings.swift in Sources */,
D0223A9B1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */,
D017495E1E118F790057C89A /* AccountStateManager.swift in Sources */,
D0FA8BB61E223C16001E855B /* SecretApiLayer8.swift in Sources */,
D0B843C71DA7FF30005F29E1 /* NBPhoneNumberDefines.m in Sources */,
@ -1794,6 +1814,7 @@
D03C53711DAD5CA9004C17B3 /* CachedGroupParticipants.swift in Sources */,
C2366C841E4F3EAA0097CCFF /* GroupReturnAndLeft.swift in Sources */,
D03C53671DAD5CA9004C17B3 /* ApiUtils.swift in Sources */,
D0223A9C1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */,
D0AAD1B91E326FE200D5B9DE /* ManagedAutoremoveMessageOperations.swift in Sources */,
D001F3F21E128A1C007A8C60 /* UpdateGroup.swift in Sources */,
D0F7B1EB1E045C87007EB8A5 /* ResolvePeerByName.swift in Sources */,
@ -1834,6 +1855,7 @@
D0F3A8A61E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */,
D00D34431E6EDD2E0057B307 /* ManagedSynchronizeConsumeMessageContentsOperations.swift in Sources */,
D049EAE91E44B67100A2CD3A /* RecentPeerItem.swift in Sources */,
D0FA35091EA632E400E56FFA /* CollectCacheUsageStats.swift in Sources */,
D001F3F31E128A1C007A8C60 /* UpdateMessageService.swift in Sources */,
D0C50E351E93A86600F62E39 /* CallSessionManager.swift in Sources */,
D0B8442D1DAB91E0005F29E1 /* NBMetadataCoreTest.m in Sources */,
@ -1891,6 +1913,7 @@
D0DC35511DE36908000195EB /* RequestChatContextResults.swift in Sources */,
D0F7B1EC1E045C87007EB8A5 /* SearchPeers.swift in Sources */,
D001F3EF1E128A1C007A8C60 /* AccountIntermediateState.swift in Sources */,
D0223A991EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */,
D00C7CCD1E3620C30080C3D5 /* CachedChannelParticipants.swift in Sources */,
D03C536E1DAD5CA9004C17B3 /* PhoneNumber.swift in Sources */,
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
@ -1900,6 +1923,7 @@
D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */,
D073CEA41DCBF3EA007511FD /* MultipartUpload.swift in Sources */,
D03C53701DAD5CA9004C17B3 /* ExportedInvitation.swift in Sources */,
D0FA35061EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */,
D08F4A671E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */,
D05A32E81E6F0B5C002760B4 /* RecentAccountSession.swift in Sources */,
D0F7B1E31E045C7B007EB8A5 /* RichText.swift in Sources */,

View File

@ -145,7 +145,7 @@ public class UnauthorizedAccount {
postbox.removeKeychainEntryForKey(key)
})
return initializedNetwork(apiId: self.apiId, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment)
return initializedNetwork(apiId: self.apiId, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment)
|> map { network in
let updated = UnauthorizedAccount(apiId: self.apiId, id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
@ -219,6 +219,7 @@ private var declaredEncodables: Void = {
declareEncodable(ArchivedStickerPacksInfo.self, f: { ArchivedStickerPacksInfo(decoder: $0) })
declareEncodable(SynchronizeChatInputStateOperation.self, f: { SynchronizeChatInputStateOperation(decoder: $0) })
declareEncodable(SynchronizeSavedGifsOperation.self, f: { SynchronizeSavedGifsOperation(decoder: $0) })
declareEncodable(CacheStorageSettings.self, f: { CacheStorageSettings(decoder: $0) })
return
}()
@ -272,12 +273,12 @@ public func accountWithId(apiId: Int32, id: AccountRecordId, supplementary: Bool
if let accountState = accountState {
switch accountState {
case let unauthorizedState as UnauthorizedAccountState:
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: path), testingEnvironment: testingEnvironment)
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: testingEnvironment)
|> map { network -> AccountResult in
return .unauthorized(UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: path, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
}
case let authorizedState as AuthorizedAccountState:
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: path), testingEnvironment: testingEnvironment)
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: testingEnvironment)
|> map { network -> AccountResult in
return .authorized(Account(id: id, basePath: path, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods))
}
@ -286,7 +287,7 @@ public func accountWithId(apiId: Int32, id: AccountRecordId, supplementary: Bool
}
}
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: path), testingEnvironment: testingEnvironment)
return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: testingEnvironment)
|> map { network -> AccountResult in
return .unauthorized(UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: path, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
}
@ -365,9 +366,9 @@ public enum AccountNetworkState {
public final class AccountAuxiliaryMethods {
public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?
public let fetchResource: (Account, MediaResource, Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>?
public let fetchResource: (Account, MediaResource, Range<Int>, MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>?
public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>?) {
public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Range<Int>, MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>?) {
self.updatePeerChatInputState = updatePeerChatInputState
self.fetchResource = fetchResource
}
@ -624,20 +625,6 @@ public class Account {
self.managedOperationsDisposable.dispose()
}
/*public func currentNetworkStats() -> Signal<MTNetworkUsageManagerStats, NoError> {
return Signal { subscriber in
let manager = MTNetworkUsageManager(info: MTNetworkUsageCalculationInfo(filePath: accountNetworkUsageInfoPath(basePath: self.basePath)))!
manager.currentStats().start(next: { next in
if let stats = next as? MTNetworkUsageManagerStats {
subscriber.putNext(stats)
}
subscriber.putCompletion()
}, error: nil, completed: nil)
return EmptyDisposable
}
}*/
public func peerInputActivities(peerId: PeerId) -> Signal<[(PeerId, PeerInputActivity)], NoError> {
return self.peerInputActivityManager.activities(peerId: peerId)
}
@ -653,15 +640,19 @@ public class Account {
}
}
public func accountNetworkUsageStats(account: Account, reset: ResetNetworkUsageStats) -> Signal<NetworkUsageStats, NoError> {
return networkUsageStats(basePath: account.basePath, reset: reset)
}
public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ resourceData: MediaResourceData, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: Media, _ userInteractive: Bool) -> Signal<Media?, NoError>
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil) {
account.postbox.mediaBox.fetchResource = { [weak account] resource, range -> Signal<MediaResourceDataFetchResult, NoError> in
account.postbox.mediaBox.fetchResource = { [weak account] resource, range, tag -> Signal<MediaResourceDataFetchResult, NoError> in
if let strongAccount = account {
if let result = fetchResource(account: strongAccount, resource: resource, range: range) {
if let result = fetchResource(account: strongAccount, resource: resource, range: range, tag: tag) {
return result
} else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, range) {
} else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, range, tag) {
return result
} else {
return .never()

View File

@ -130,6 +130,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[393186209] = { return Api.SendMessageAction.parse_sendMessageGeoLocationAction($0) }
dict[1653390447] = { return Api.SendMessageAction.parse_sendMessageChooseContactAction($0) }
dict[-580219064] = { return Api.SendMessageAction.parse_sendMessageGamePlayAction($0) }
dict[-1997373508] = { return Api.SendMessageAction.parse_sendMessageRecordRoundAction($0) }
dict[-1150187996] = { return Api.SendMessageAction.parse_sendMessageUploadRoundAction($0) }
dict[-1137792208] = { return Api.PrivacyKey.parse_privacyKeyStatusTimestamp($0) }
dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) }
dict[1030105979] = { return Api.PrivacyKey.parse_privacyKeyPhoneCall($0) }
@ -4875,6 +4877,8 @@ public struct Api {
case sendMessageGeoLocationAction
case sendMessageChooseContactAction
case sendMessageGamePlayAction
case sendMessageRecordRoundAction
case sendMessageUploadRoundAction
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool {
switch self {
@ -4943,6 +4947,18 @@ public struct Api {
buffer.appendInt32(-580219064)
}
break
case .sendMessageRecordRoundAction:
if boxed {
buffer.appendInt32(-1997373508)
}
break
case .sendMessageUploadRoundAction:
if boxed {
buffer.appendInt32(-1150187996)
}
break
}
return true
@ -5013,6 +5029,12 @@ public struct Api {
fileprivate static func parse_sendMessageGamePlayAction(_ reader: BufferReader) -> SendMessageAction? {
return Api.SendMessageAction.sendMessageGamePlayAction
}
fileprivate static func parse_sendMessageRecordRoundAction(_ reader: BufferReader) -> SendMessageAction? {
return Api.SendMessageAction.sendMessageRecordRoundAction
}
fileprivate static func parse_sendMessageUploadRoundAction(_ reader: BufferReader) -> SendMessageAction? {
return Api.SendMessageAction.sendMessageUploadRoundAction
}
public var description: String {
get {
@ -5039,6 +5061,10 @@ public struct Api {
return "(sendMessageChooseContactAction)"
case .sendMessageGamePlayAction:
return "(sendMessageGamePlayAction)"
case .sendMessageRecordRoundAction:
return "(sendMessageRecordRoundAction)"
case .sendMessageUploadRoundAction:
return "(sendMessageUploadRoundAction)"
}
}
}

View File

@ -0,0 +1,58 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
#else
import Postbox
import SwiftSignalKit
#endif
public struct CacheStorageSettings: PreferencesEntry, Equatable {
public let defaultCacheStorageTimeout: Int32
public static var defaultSettings: CacheStorageSettings {
return CacheStorageSettings(defaultCacheStorageTimeout: Int32.max)
}
init(defaultCacheStorageTimeout: Int32) {
self.defaultCacheStorageTimeout = defaultCacheStorageTimeout
}
public init(decoder: Decoder) {
self.defaultCacheStorageTimeout = decoder.decodeInt32ForKey("dt") as Int32
}
public func encode(_ encoder: Encoder) {
encoder.encodeInt32(self.defaultCacheStorageTimeout, forKey: "dt")
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? CacheStorageSettings {
return self == to
} else {
return false
}
}
public static func ==(lhs: CacheStorageSettings, rhs: CacheStorageSettings) -> Bool {
return lhs.defaultCacheStorageTimeout == rhs.defaultCacheStorageTimeout
}
public func withUpdatedDefaultCacheStorageTimeout(_ defaultCacheStorageTimeout: Int32) -> CacheStorageSettings {
return CacheStorageSettings(defaultCacheStorageTimeout: defaultCacheStorageTimeout)
}
}
public func updateCacheStorageSettingsInteractively(postbox: Postbox, _ f: @escaping (CacheStorageSettings) -> CacheStorageSettings) -> Signal<Void, NoError> {
return postbox.modify { modifier -> Void in
modifier.updatePreferencesEntry(key: PreferencesKeys.cacheStorageSettings, { entry in
let currentSettings: CacheStorageSettings
if let entry = entry as? CacheStorageSettings {
currentSettings = entry
} else {
currentSettings = CacheStorageSettings.defaultSettings
}
return f(currentSettings)
})
}
}

View File

@ -5,10 +5,6 @@ import Foundation
import Postbox
#endif
public protocol TelegramMediaResource: MediaResource, Coding {
func isEqual(to: TelegramMediaResource) -> Bool
}
protocol TelegramCloudMediaResource: TelegramMediaResource {
var datacenterId: Int { get }
var apiInputLocation: Api.InputFileLocation { get }

View File

@ -0,0 +1,159 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
#else
import Postbox
import SwiftSignalKit
#endif
public enum PeerCacheUsageCategory: Int32 {
case image = 0
case video
case audio
case file
}
public struct CacheUsageStats {
public let media: [PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]]
public let mediaResourceIds: [MediaId: [MediaResourceId]]
public let peers: [PeerId: Peer]
public init(media: [PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]], mediaResourceIds: [MediaId: [MediaResourceId]], peers: [PeerId: Peer]) {
self.media = media
self.mediaResourceIds = mediaResourceIds
self.peers = peers
}
}
public enum CacheUsageStatsResult {
case progress(Float)
case result(CacheUsageStats)
}
private enum CollectCacheUsageStatsError {
case done(CacheUsageStats)
}
private final class CacheUsageStatsState {
var media: [PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]] = [:]
var mediaResourceIds: [MediaId: [MediaResourceId]] = [:]
var lowerBound: MessageIndex?
}
public func collectCacheUsageStats(account: Account) -> Signal<CacheUsageStatsResult, NoError> {
let state = Atomic<CacheUsageStatsState>(value: CacheUsageStatsState())
let fetch = account.postbox.modify { modifier -> ([PeerId : Set<MediaId>], [MediaId : Media], MessageIndex?) in
return modifier.enumerateMedia(lowerBound: state.with { $0.lowerBound }, limit: 1000)
} |> mapError { _ -> CollectCacheUsageStatsError in preconditionFailure() }
let process: ([PeerId : Set<MediaId>], [MediaId : Media], MessageIndex?) -> Signal<CacheUsageStatsResult, CollectCacheUsageStatsError> = { mediaByPeer, mediaRefs, updatedLowerBound in
var mediaIdToPeerId: [MediaId: PeerId] = [:]
for (peerId, mediaIds) in mediaByPeer {
for id in mediaIds {
mediaIdToPeerId[id] = peerId
}
}
var resourceIdToMediaId: [WrappedMediaResourceId: (MediaId, PeerCacheUsageCategory)] = [:]
var mediaResourceIds: [MediaId: [MediaResourceId]] = [:]
var resourceIds: [MediaResourceId] = []
for (id, media) in mediaRefs {
mediaResourceIds[id] = []
switch media {
case let image as TelegramMediaImage:
for representation in image.representations {
resourceIds.append(representation.resource.id)
resourceIdToMediaId[WrappedMediaResourceId(representation.resource.id)] = (id, .image)
mediaResourceIds[id]!.append(representation.resource.id)
}
case let file as TelegramMediaFile:
var category: PeerCacheUsageCategory = .file
loop: for attribute in file.attributes {
switch attribute {
case .Video:
category = .video
break loop
case .Audio:
category = .audio
break loop
default:
break
}
}
for representation in file.previewRepresentations {
resourceIds.append(representation.resource.id)
resourceIdToMediaId[WrappedMediaResourceId(representation.resource.id)] = (id, category)
mediaResourceIds[id]!.append(representation.resource.id)
}
resourceIds.append(file.resource.id)
resourceIdToMediaId[WrappedMediaResourceId(file.resource.id)] = (id, category)
mediaResourceIds[id]!.append(file.resource.id)
default:
break
}
}
return account.postbox.mediaBox.collectResourceCacheUsage(resourceIds)
|> mapError { _ -> CollectCacheUsageStatsError in preconditionFailure() }
|> mapToSignal { result -> Signal<CacheUsageStatsResult, CollectCacheUsageStatsError> in
state.with { state -> Void in
state.lowerBound = updatedLowerBound
for (wrappedId, size) in result {
if let (id, category) = resourceIdToMediaId[wrappedId] {
if let peerId = mediaIdToPeerId[id] {
if state.media[peerId] == nil {
state.media[peerId] = [:]
}
if state.media[peerId]![category] == nil {
state.media[peerId]![category] = [:]
}
var currentSize: Int64 = 0
if let current = state.media[peerId]![category]![id] {
currentSize = current
}
state.media[peerId]![category]![id] = currentSize + size
}
}
}
for (id, ids) in mediaResourceIds {
state.mediaResourceIds[id] = ids
}
}
if updatedLowerBound == nil {
let (finalMedia, finalMediaResourceIds) = state.with { state -> ([PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]], [MediaId: [MediaResourceId]]) in
return (state.media, state.mediaResourceIds)
}
return account.postbox.modify { modifier -> CacheUsageStats in
var peers: [PeerId: Peer] = [:]
for peerId in finalMedia.keys {
if let peer = modifier.getPeer(peerId) {
peers[peer.id] = peer
}
}
return CacheUsageStats(media: finalMedia, mediaResourceIds: finalMediaResourceIds, peers: peers)
} |> mapError { _ -> CollectCacheUsageStatsError in preconditionFailure() }
|> mapToSignal { stats -> Signal<CacheUsageStatsResult, CollectCacheUsageStatsError> in
return .fail(.done(stats))
}
} else {
return .complete()
}
}
}
let signal = (fetch |> mapToSignal { mediaByPeer, mediaRefs, updatedLowerBound -> Signal<CacheUsageStatsResult, CollectCacheUsageStatsError> in
return process(mediaByPeer, mediaRefs, updatedLowerBound)
}) |> restart
return signal |> `catch` { error in
switch error {
case let .done(result):
return .single(.result(result))
}
}
}
public func clearCachedMediaResources(account: Account, mediaResourceIds: Set<WrappedMediaResourceId>) -> Signal<Void, NoError> {
return account.postbox.mediaBox.removeCachedResources(mediaResourceIds)
}

View File

@ -28,11 +28,11 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
let mtProto: MTProto
let requestService: MTRequestMessageService
init(datacenterId: Int, context: MTContext, masterDatacenterId: Int) {
init(datacenterId: Int, context: MTContext, masterDatacenterId: Int, usageInfo: MTNetworkUsageCalculationInfo?) {
self.datacenterId = datacenterId
self.context = context
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: nil)
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: usageInfo)
if datacenterId != masterDatacenterId {
self.mtProto.authTokenMasterDatacenterId = masterDatacenterId
self.mtProto.requiredAuthToken = Int(datacenterId) as NSNumber

View File

@ -9,8 +9,8 @@ import SwiftSignalKit
import Photos
#endif
private func fetchCloudMediaLocation(account: Account, resource: TelegramCloudMediaResource, size: Int?, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError> {
return multipartFetch(account: account, resource: resource, size: size, range: range)
private func fetchCloudMediaLocation(account: Account, resource: TelegramCloudMediaResource, size: Int?, range: Range<Int>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError> {
return multipartFetch(account: account, resource: resource, size: size, range: range, tag: tag)
}
private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaResourceDataFetchResult, NoError> {
@ -30,13 +30,13 @@ private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaRes
}
}
func fetchResource(account: Account, resource: MediaResource, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>? {
func fetchResource(account: Account, resource: MediaResource, range: Range<Int>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>? {
if let _ = resource as? EmptyMediaResource {
return .never()
} else if let secretFileResource = resource as? SecretFileMediaResource {
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range))
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range, tag: tag))
} else if let cloudResource = resource as? TelegramCloudMediaResource {
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range))
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range, tag: tag))
} else if let localFileResource = resource as? LocalFileReferenceMediaResource {
if false {
//return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchLocalFileResource(path: localFileResource.localFilePath) |> delay(10.0, queue: Queue.concurrentDefaultQueue()))

View File

@ -9,6 +9,6 @@ import Foundation
import MtProtoKitDynamic
#endif
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError> {
return multipartFetch(account: account, resource: resource, size: resource.size, range: range, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, range: Range<Int>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError> {
return multipartFetch(account: account, resource: resource, size: resource.size, range: range, tag: tag, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
}

View File

@ -95,6 +95,10 @@ private func actionFromActivity(_ activity: PeerInputActivity?) -> Api.SendMessa
return .sendMessageGamePlayAction
case let .uploadingFile(progress):
return .sendMessageUploadDocumentAction(progress: progress)
case .recordingInstantVideo:
return .sendMessageRecordRoundAction
case .uploadingInstantVideo:
return .sendMessageUploadRoundAction
}
} else {
return .sendMessageCancelAction

View File

@ -0,0 +1,21 @@
#if os(macOS)
import PostboxMac
#else
import Postbox
#endif
public enum MediaResourceStatsCategory {
case generic
case image
case video
case audio
case file
}
public final class TelegramMediaResourceFetchTag: MediaResourceFetchTag {
public let statsCategory: MediaResourceStatsCategory
public init(statsCategory: MediaResourceStatsCategory) {
self.statsCategory = statsCategory
}
}

View File

@ -192,8 +192,8 @@ private final class MultipartFetchManager {
}
}
func multipartFetch(account: Account, resource: TelegramCloudMediaResource, size: Int?, range: Range<Int>, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal<MediaResourceDataFetchResult, NoError> {
return account.network.download(datacenterId: resource.datacenterId)
func multipartFetch(account: Account, resource: TelegramCloudMediaResource, size: Int?, range: Range<Int>, tag: MediaResourceFetchTag?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal<MediaResourceDataFetchResult, NoError> {
return account.network.download(datacenterId: resource.datacenterId, tag: tag)
|> mapToSignal { download -> Signal<MediaResourceDataFetchResult, NoError> in
return Signal { subscriber in
let inputLocation = resource.apiInputLocation

View File

@ -322,8 +322,8 @@ enum MultipartUploadSource {
case data(Data)
}
func multipartUpload(network: Network, postbox: Postbox, source: MultipartUploadSource, encrypt: Bool, hintFileSize: Int? = nil) -> Signal<MultipartUploadResult, NoError> {
return network.download(datacenterId: network.datacenterId)
func multipartUpload(network: Network, postbox: Postbox, source: MultipartUploadSource, encrypt: Bool, tag: MediaResourceFetchTag?, hintFileSize: Int? = nil) -> Signal<MultipartUploadResult, NoError> {
return network.download(datacenterId: network.datacenterId, tag: tag)
|> mapToSignal { download -> Signal<MultipartUploadResult, NoError> in
return Signal { subscriber in
var encryptionKey: SecretFileEncryptionKey?
@ -348,7 +348,7 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload
case let .resource(resource):
dataSignal = postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: true)) |> map { MultipartUploadData.resourceData($0) }
headerSize = resource.headerSize
fetchedResource = postbox.mediaBox.fetchedResource(resource) |> map {_ in}
fetchedResource = postbox.mediaBox.fetchedResource(resource, tag: tag) |> map {_ in}
case let .data(data):
dataSignal = .single(.data(data))
headerSize = 0

View File

@ -84,6 +84,7 @@ struct OperationLogTags {
private enum PreferencesKeyValues: Int32 {
case globalNotifications = 0
case cacheStorageSettings = 1
}
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -98,4 +99,10 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.globalNotifications.rawValue)
return key
}()
public static let cacheStorageSettings: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.cacheStorageSettings.rawValue)
return key
}()
}

View File

@ -94,7 +94,206 @@ private var registeredLoggingFunctions: Void = {
registerLoggingFunctions()
}()
func initializedNetwork(apiId: Int32, supplementary: Bool, datacenterId: Int, keychain: Keychain, networkUsageInfoPath: String?, testingEnvironment: Bool) -> Signal<Network, NoError> {
private enum UsageCalculationConnection: Int32 {
case cellular = 0
case wifi = 1
}
private enum UsageCalculationDirection: Int32 {
case incoming = 0
case outgoing = 1
}
private struct UsageCalculationTag {
let connection: UsageCalculationConnection
let direction: UsageCalculationDirection
let category: MediaResourceStatsCategory
var key: Int32 {
switch category {
case .generic:
return 0 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
case .image:
return 1 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
case .video:
return 2 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
case .audio:
return 3 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
case .file:
return 4 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
}
}
}
private enum UsageCalculationResetKey: Int32 {
case wifi = 80 //20 * 4 + 0
case cellular = 81 //20 * 4 + 2
}
private func usageCalculationInfo(basePath: String, category: MediaResourceStatsCategory?) -> MTNetworkUsageCalculationInfo {
let categoryValue: MediaResourceStatsCategory
if let category = category {
categoryValue = category
} else {
categoryValue = .generic
}
return MTNetworkUsageCalculationInfo(filePath: basePath + "/network-stats", incomingWWANKey: UsageCalculationTag(connection: .cellular, direction: .incoming, category: categoryValue).key, outgoingWWANKey: UsageCalculationTag(connection: .cellular, direction: .outgoing, category: categoryValue).key, incomingOtherKey: UsageCalculationTag(connection: .wifi, direction: .incoming, category: categoryValue).key, outgoingOtherKey: UsageCalculationTag(connection: .wifi, direction: .outgoing, category: categoryValue).key)
}
public struct NetworkUsageStatsDirectionsEntry: Equatable {
public let incoming: Int64
public let outgoing: Int64
public static func ==(lhs: NetworkUsageStatsDirectionsEntry, rhs: NetworkUsageStatsDirectionsEntry) -> Bool {
return lhs.incoming == rhs.incoming && lhs.outgoing == rhs.outgoing
}
}
public struct NetworkUsageStatsConnectionsEntry: Equatable {
public let cellular: NetworkUsageStatsDirectionsEntry
public let wifi: NetworkUsageStatsDirectionsEntry
public static func ==(lhs: NetworkUsageStatsConnectionsEntry, rhs: NetworkUsageStatsConnectionsEntry) -> Bool {
return lhs.cellular == rhs.cellular && lhs.wifi == rhs.wifi
}
}
public struct NetworkUsageStats: Equatable {
public let generic: NetworkUsageStatsConnectionsEntry
public let image: NetworkUsageStatsConnectionsEntry
public let video: NetworkUsageStatsConnectionsEntry
public let audio: NetworkUsageStatsConnectionsEntry
public let file: NetworkUsageStatsConnectionsEntry
public let resetWifiTimestamp: Int32
public let resetCellularTimestamp: Int32
public static func ==(lhs: NetworkUsageStats, rhs: NetworkUsageStats) -> Bool {
return lhs.generic == rhs.generic && lhs.image == rhs.image && lhs.video == rhs.video && lhs.audio == rhs.audio && lhs.file == rhs.file && lhs.resetWifiTimestamp == rhs.resetWifiTimestamp && lhs.resetCellularTimestamp == rhs.resetCellularTimestamp
}
}
public struct ResetNetworkUsageStats: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let wifi = ResetNetworkUsageStats(rawValue: 1 << 0)
public static let cellular = ResetNetworkUsageStats(rawValue: 1 << 1)
}
func networkUsageStats(basePath: String, reset: ResetNetworkUsageStats) -> Signal<NetworkUsageStats, NoError> {
return ((Signal<NetworkUsageStats, NoError> { subscriber in
let info = usageCalculationInfo(basePath: basePath, category: nil)
let manager = MTNetworkUsageManager(info: info)!
let rawKeys: [UsageCalculationTag] = [
UsageCalculationTag(connection: .cellular, direction: .incoming, category: .generic),
UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .generic),
UsageCalculationTag(connection: .wifi, direction: .incoming, category: .generic),
UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .generic),
UsageCalculationTag(connection: .cellular, direction: .incoming, category: .image),
UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .image),
UsageCalculationTag(connection: .wifi, direction: .incoming, category: .image),
UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .image),
UsageCalculationTag(connection: .cellular, direction: .incoming, category: .video),
UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .video),
UsageCalculationTag(connection: .wifi, direction: .incoming, category: .video),
UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .video),
UsageCalculationTag(connection: .cellular, direction: .incoming, category: .audio),
UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .audio),
UsageCalculationTag(connection: .wifi, direction: .incoming, category: .audio),
UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .audio),
UsageCalculationTag(connection: .cellular, direction: .incoming, category: .file),
UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .file),
UsageCalculationTag(connection: .wifi, direction: .incoming, category: .file),
UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .file)
]
var keys: [NSNumber] = rawKeys.map { $0.key as NSNumber }
var resetKeys: [NSNumber] = []
var resetAddKeys: [NSNumber: NSNumber] = [:]
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
if reset.contains(.wifi) {
resetKeys = rawKeys.filter({ $0.connection == .wifi }).map({ $0.key as NSNumber })
resetAddKeys[UsageCalculationResetKey.wifi.rawValue as NSNumber] = Int64(timestamp) as NSNumber
}
if reset.contains(.cellular) {
resetKeys = rawKeys.filter({ $0.connection == .cellular }).map({ $0.key as NSNumber })
resetAddKeys[UsageCalculationResetKey.cellular.rawValue as NSNumber] = Int64(timestamp) as NSNumber
}
if !resetKeys.isEmpty {
manager.resetKeys(resetKeys, setKeys: resetAddKeys, completion: {})
}
keys.append(UsageCalculationResetKey.cellular.rawValue as NSNumber)
keys.append(UsageCalculationResetKey.wifi.rawValue as NSNumber)
let disposable = manager.currentStats(forKeys: keys).start(next: { next in
var dict: [Int32: Int64] = [:]
for key in keys {
dict[key.int32Value] = 0
}
(next as! NSDictionary).enumerateKeysAndObjects({ key, value, _ in
dict[(key as AnyObject).int32Value] = (value as AnyObject).int64Value!
})
subscriber.putNext(NetworkUsageStats(
generic: NetworkUsageStatsConnectionsEntry(
cellular: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .cellular, direction: .incoming, category: .generic).key]!,
outgoing: dict[UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .generic).key]!),
wifi: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .generic).key]!,
outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .generic).key]!)),
image: NetworkUsageStatsConnectionsEntry(
cellular: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .cellular, direction: .incoming, category: .image).key]!,
outgoing: dict[UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .image).key]!),
wifi: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .image).key]!,
outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .image).key]!)),
video: NetworkUsageStatsConnectionsEntry(
cellular: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .cellular, direction: .incoming, category: .video).key]!,
outgoing: dict[UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .video).key]!),
wifi: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .video).key]!,
outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .video).key]!)),
audio: NetworkUsageStatsConnectionsEntry(
cellular: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .cellular, direction: .incoming, category: .audio).key]!,
outgoing: dict[UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .audio).key]!),
wifi: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .audio).key]!,
outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .audio).key]!)),
file: NetworkUsageStatsConnectionsEntry(
cellular: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .cellular, direction: .incoming, category: .file).key]!,
outgoing: dict[UsageCalculationTag(connection: .cellular, direction: .outgoing, category: .file).key]!),
wifi: NetworkUsageStatsDirectionsEntry(
incoming: dict[UsageCalculationTag(connection: .wifi, direction: .incoming, category: .file).key]!,
outgoing: dict[UsageCalculationTag(connection: .wifi, direction: .outgoing, category: .file).key]!)),
resetWifiTimestamp: Int32(dict[UsageCalculationResetKey.wifi.rawValue]!),
resetCellularTimestamp: Int32(dict[UsageCalculationResetKey.cellular.rawValue]!)
))
})!
return ActionDisposable {
disposable.dispose()
}
}) |> then(Signal<NetworkUsageStats, NoError>.complete() |> delay(5.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}
func initializedNetwork(apiId: Int32, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool) -> Signal<Network, NoError> {
return Signal { subscriber in
Queue.concurrentDefaultQueue().async {
let _ = registeredLoggingFunctions
@ -131,7 +330,7 @@ func initializedNetwork(apiId: Int32, supplementary: Bool, datacenterId: Int, ke
}
context.keychain = keychain
let mtProto = MTProto(context: context, datacenterId: datacenterId, usageCalculationInfo: nil)!
let mtProto = MTProto(context: context, datacenterId: datacenterId, usageCalculationInfo: usageCalculationInfo(basePath: basePath, category: nil))!
let connectionStatus = Promise<ConnectionStatus>(.WaitingForNetwork)
@ -159,7 +358,7 @@ func initializedNetwork(apiId: Int32, supplementary: Bool, datacenterId: Int, ke
mtProto.delegate = connectionStatusDelegate
mtProto.add(requestService)
subscriber.putNext(Network(datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus))
subscriber.putNext(Network(datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath))
subscriber.putCompletion()
}
@ -172,6 +371,7 @@ public class Network: NSObject, MTRequestMessageServiceDelegate {
let context: MTContext
let mtProto: MTProto
let requestService: MTRequestMessageService
let basePath: String
private let connectionStatusDelegate: MTProtoConnectionStatusDelegate
private let _connectionStatus: Promise<ConnectionStatus>
@ -184,13 +384,14 @@ public class Network: NSObject, MTRequestMessageServiceDelegate {
var loggedOut: (() -> Void)?
fileprivate init(datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>) {
fileprivate init(datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>, basePath: String) {
self.datacenterId = datacenterId
self.context = context
self.mtProto = mtProto
self.requestService = requestService
self.connectionStatusDelegate = connectionStatusDelegate
self._connectionStatus = _connectionStatus
self.basePath = basePath
super.init()
@ -219,10 +420,10 @@ public class Network: NSObject, MTRequestMessageServiceDelegate {
self.loggedOut?()
}
func download(datacenterId: Int) -> Signal<Download, NoError> {
func download(datacenterId: Int, tag: MediaResourceFetchTag?) -> Signal<Download, NoError> {
return Signal { [weak self] subscriber in
if let strongSelf = self {
subscriber.putNext(Download(datacenterId: datacenterId, context: strongSelf.context, masterDatacenterId: strongSelf.datacenterId))
subscriber.putNext(Download(datacenterId: datacenterId, context: strongSelf.context, masterDatacenterId: strongSelf.datacenterId, usageInfo: usageCalculationInfo(basePath: strongSelf.basePath, category: (tag as? TelegramMediaResourceFetchTag)?.statsCategory)))
}
subscriber.putCompletion()

View File

@ -5,6 +5,8 @@ public enum PeerInputActivity: Comparable {
case uploadingFile(progress: Int32)
case recordingVoice
case playingGame
case recordingInstantVideo
case uploadingInstantVideo
public static func ==(lhs: PeerInputActivity, rhs: PeerInputActivity) -> Bool {
switch lhs {
@ -32,6 +34,18 @@ public enum PeerInputActivity: Comparable {
} else {
return false
}
case .recordingInstantVideo:
if case .recordingInstantVideo = rhs {
return true
} else {
return false
}
case .uploadingInstantVideo:
if case .uploadingInstantVideo = rhs {
return true
} else {
return false
}
}
}
@ -43,8 +57,12 @@ public enum PeerInputActivity: Comparable {
return 1
case .recordingVoice:
return 2
case .playingGame:
case .recordingInstantVideo:
return 3
case .uploadingInstantVideo:
return 4
case .playingGame:
return 5
}
}
@ -70,6 +88,10 @@ extension PeerInputActivity {
self = .uploadingFile(progress: progress)
case let .sendMessageUploadVideoAction(progress):
self = .uploadingFile(progress: progress)
case .sendMessageRecordRoundAction:
self = .recordingInstantVideo
case .sendMessageUploadRoundAction:
self = .uploadingInstantVideo
}
}
}

View File

@ -25,7 +25,7 @@ public func updateAccountPhoto(account:Account, resource:MediaResource) -> Signa
public func updatePeerPhoto(account:Account, peerId:PeerId, resource:MediaResource) -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> {
return account.postbox.loadedPeerWithId(peerId) |> mapError {_ in return .generic} |> mapToSignal { peer in
return multipartUpload(network: account.network, postbox: account.postbox, source: .resource(resource), encrypt: false)
return multipartUpload(network: account.network, postbox: account.postbox, source: .resource(resource), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|> mapError {_ in return .generic}
|> mapToSignal { result -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
switch result {
@ -89,7 +89,12 @@ public func updatePeerPhoto(account:Account, peerId:PeerId, resource:MediaResour
for chat in updates.chats {
if chat.peerId == peerId {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
return .single(.complete(groupOrChannel.profileImageRepresentations))
return account.postbox.modify { modifier -> UpdatePeerPhotoStatus in
updatePeers(modifier: modifier, peers: [groupOrChannel], update: { _, updated in
return updated
})
return .complete(groupOrChannel.profileImageRepresentations)
} |> mapError { _ in return .generic }
}
}
}

View File

@ -74,7 +74,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin
private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerId: PeerId, image: TelegramMediaImage, text: String) -> Signal<PendingMessageUploadedContentResult, NoError> {
if let largestRepresentation = largestImageRepresentation(image.representations) {
return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat)
return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|> map { next -> PendingMessageUploadedContentResult in
switch next {
case let .progress(progress):
@ -153,7 +153,7 @@ private enum UploadedMediaThumbnail {
}
private func uploadedThumbnail(network: Network, postbox: Postbox, image: TelegramMediaImageRepresentation) -> Signal<Api.InputFile?, NoError> {
return multipartUpload(network: network, postbox: postbox, source: .resource(image.resource), encrypt: false)
return multipartUpload(network: network, postbox: postbox, source: .resource(image.resource), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|> mapToSignal { result -> Signal<Api.InputFile?, NoError> in
switch result {
case .progress:
@ -166,6 +166,20 @@ private func uploadedThumbnail(network: Network, postbox: Postbox, image: Telegr
}
}
public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileAttribute]) -> MediaResourceStatsCategory {
for attribute in attributes {
switch attribute {
case .Audio:
return .audio
case .Video:
return .video
default:
break
}
}
return .file
}
private func uploadedMediaFileContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal<PendingMessageUploadedContentResult, NoError> {
var hintSize: Int?
if let size = file.size {
@ -173,7 +187,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf
} else if let resource = file.resource as? LocalFileReferenceMediaResource, let size = resource.size {
hintSize = Int(size)
}
let upload = multipartUpload(network: network, postbox: postbox, source: .resource(file.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, hintFileSize: hintSize)
let upload = multipartUpload(network: network, postbox: postbox, source: .resource(file.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(file.attributes)), hintFileSize: hintSize)
/*|> map { next -> UploadedMediaFileContent in
switch next {
case let .progress(progress):

View File

@ -140,7 +140,7 @@ private enum UploadMediaEvent {
}
private func uploadedImage(account: Account, text: String, data: Data) -> Signal<UploadMediaEvent, NoError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false)
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|> map { next -> UploadMediaEvent in
switch next {
case let .inputFile(inputFile):
@ -154,7 +154,7 @@ private func uploadedImage(account: Account, text: String, data: Data) -> Signal
}
private func uploadedFile(account: Account, text: String, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal<UploadMediaEvent, NoError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false)
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)))
|> map { next -> UploadMediaEvent in
switch next {
case let .inputFile(inputFile):

View File

@ -1,9 +1,26 @@
public func dataSizeString(_ size: Int) -> String {
if size >= 1024 * 1024 {
return "\(size / (1024 * 1024)) MB"
if size >= 1024 * 1024 * 1024 {
let remainder = (size % (1024 * 1024 * 1024)) / (1024 * 1024 * 102)
if remainder != 0 {
return "\(size / (1024 * 1024 * 1024)),\(remainder) GB"
} else {
return "\(size / (1024 * 1024 * 1024)) GB"
}
} else if size >= 1024 * 1024 {
let remainder = (size % (1024 * 1024)) / (1024 * 102)
if remainder != 0 {
return "\(size / (1024 * 1024)),\(remainder) MB"
} else {
return "\(size / (1024 * 1024)) MB"
}
} else if size >= 1024 {
return "\(size / 1024) KB"
let remainder = (size % (1024)) / (102)
if remainder != 0 {
return "\(size / 1024),\(remainder) KB"
} else {
return "\(size / 1024) KB"
}
} else {
return "\(size) B"
}

View File

@ -0,0 +1,11 @@
import Foundation
#if os(macOS)
import PostboxMac
#else
import Postbox
#endif
public protocol TelegramMediaResource: MediaResource, Coding {
func isEqual(to: TelegramMediaResource) -> Bool
}