import Foundation import Postbox import SwiftSignalKit import TelegramCore public struct AutomaticMediaDownloadCategory: PostboxCoding, Equatable { public var cellular: Bool public var wifi: Bool public var sizeLimit: Int32 public init(cellular: Bool, wifi: Bool, sizeLimit: Int32) { self.cellular = cellular self.wifi = wifi self.sizeLimit = sizeLimit } public init(decoder: PostboxDecoder) { self.cellular = decoder.decodeInt32ForKey("cellular", orElse: 0) != 0 self.wifi = decoder.decodeInt32ForKey("wifi", orElse: 0) != 0 self.sizeLimit = decoder.decodeInt32ForKey("sizeLimit", orElse: 0) } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.cellular ? 1 : 0, forKey: "cellular") encoder.encodeInt32(self.wifi ? 1 : 0, forKey: "wifi") encoder.encodeInt32(self.sizeLimit, forKey: "sizeLimit") } } public struct AutomaticMediaDownloadCategories: Equatable, PostboxCoding { public var photo: AutomaticMediaDownloadCategory public var video: AutomaticMediaDownloadCategory public var file: AutomaticMediaDownloadCategory public var voiceMessage: AutomaticMediaDownloadCategory public var videoMessage: AutomaticMediaDownloadCategory public init(photo: AutomaticMediaDownloadCategory, video: AutomaticMediaDownloadCategory, file: AutomaticMediaDownloadCategory, voiceMessage: AutomaticMediaDownloadCategory, videoMessage: AutomaticMediaDownloadCategory) { self.photo = photo self.video = video self.file = file self.voiceMessage = voiceMessage self.videoMessage = videoMessage } public init(decoder: PostboxDecoder) { self.photo = decoder.decodeObjectForKey("photo", decoder: AutomaticMediaDownloadCategory.init(decoder:)) as! AutomaticMediaDownloadCategory self.video = decoder.decodeObjectForKey("video", decoder: AutomaticMediaDownloadCategory.init(decoder:)) as! AutomaticMediaDownloadCategory self.file = decoder.decodeObjectForKey("file", decoder: AutomaticMediaDownloadCategory.init(decoder:)) as! AutomaticMediaDownloadCategory self.voiceMessage = decoder.decodeObjectForKey("voiceMessage", decoder: AutomaticMediaDownloadCategory.init(decoder:)) as! AutomaticMediaDownloadCategory self.videoMessage = decoder.decodeObjectForKey("videoMessage", decoder: AutomaticMediaDownloadCategory.init(decoder:)) as! AutomaticMediaDownloadCategory } public func encode(_ encoder: PostboxEncoder) { encoder.encodeObject(self.photo, forKey: "photo") encoder.encodeObject(self.video, forKey: "video") encoder.encodeObject(self.file, forKey: "file") encoder.encodeObject(self.voiceMessage, forKey: "voiceMessage") encoder.encodeObject(self.videoMessage, forKey: "videoMessage") } } public struct AutomaticMediaDownloadPeers: Equatable, PostboxCoding { public var contacts: AutomaticMediaDownloadCategories public var otherPrivate: AutomaticMediaDownloadCategories public var groups: AutomaticMediaDownloadCategories public var channels: AutomaticMediaDownloadCategories public init(contacts: AutomaticMediaDownloadCategories, otherPrivate: AutomaticMediaDownloadCategories, groups: AutomaticMediaDownloadCategories, channels: AutomaticMediaDownloadCategories) { self.contacts = contacts self.otherPrivate = otherPrivate self.groups = groups self.channels = channels } public init(decoder: PostboxDecoder) { self.contacts = decoder.decodeObjectForKey("contacts", decoder: AutomaticMediaDownloadCategories.init(decoder:)) as! AutomaticMediaDownloadCategories self.otherPrivate = decoder.decodeObjectForKey("otherPrivate", decoder: AutomaticMediaDownloadCategories.init(decoder:)) as! AutomaticMediaDownloadCategories self.groups = decoder.decodeObjectForKey("groups", decoder: AutomaticMediaDownloadCategories.init(decoder:)) as! AutomaticMediaDownloadCategories self.channels = decoder.decodeObjectForKey("channels", decoder: AutomaticMediaDownloadCategories.init(decoder:)) as! AutomaticMediaDownloadCategories } public func encode(_ encoder: PostboxEncoder) { encoder.encodeObject(self.contacts, forKey: "contacts") encoder.encodeObject(self.otherPrivate, forKey: "otherPrivate") encoder.encodeObject(self.groups, forKey: "groups") encoder.encodeObject(self.channels, forKey: "channels") } } public struct AutomaticMediaDownloadSettings: PreferencesEntry, Equatable { public var masterEnabled: Bool public var peers: AutomaticMediaDownloadPeers public var autoplayGifs: Bool public var saveIncomingPhotos: Bool public static var defaultSettings: AutomaticMediaDownloadSettings { let defaultCategory = AutomaticMediaDownloadCategories( photo: AutomaticMediaDownloadCategory(cellular: true, wifi: true, sizeLimit: Int32.max), video: AutomaticMediaDownloadCategory(cellular: false, wifi: false, sizeLimit: 1 * 1024 * 1024), file: AutomaticMediaDownloadCategory(cellular: false, wifi: false, sizeLimit: 1 * 1024 * 1024), voiceMessage: AutomaticMediaDownloadCategory(cellular: true, wifi: true, sizeLimit: Int32.max), videoMessage: AutomaticMediaDownloadCategory(cellular: false, wifi: false, sizeLimit: Int32.max) ) return AutomaticMediaDownloadSettings(masterEnabled: true, peers: AutomaticMediaDownloadPeers( contacts: defaultCategory, otherPrivate: defaultCategory, groups: defaultCategory, channels: defaultCategory ), autoplayGifs: true, saveIncomingPhotos: false) } init(masterEnabled: Bool, peers: AutomaticMediaDownloadPeers, autoplayGifs: Bool, saveIncomingPhotos: Bool) { self.masterEnabled = masterEnabled self.peers = peers self.autoplayGifs = autoplayGifs self.saveIncomingPhotos = saveIncomingPhotos } public init(decoder: PostboxDecoder) { self.masterEnabled = decoder.decodeInt32ForKey("masterEnabled", orElse: 1) != 0 self.peers = (decoder.decodeObjectForKey("peers", decoder: AutomaticMediaDownloadPeers.init(decoder:)) as? AutomaticMediaDownloadPeers) ?? AutomaticMediaDownloadSettings.defaultSettings.peers self.autoplayGifs = decoder.decodeInt32ForKey("autoplayGifs", orElse: 1) != 0 self.saveIncomingPhotos = decoder.decodeInt32ForKey("siph", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.masterEnabled ? 1 : 0, forKey: "autoplayGifs") encoder.encodeObject(self.peers, forKey: "peers") encoder.encodeInt32(self.autoplayGifs ? 1 : 0, forKey: "autoplayGifs") encoder.encodeInt32(self.saveIncomingPhotos ? 1 : 0, forKey: "siph") } public func isEqual(to: PreferencesEntry) -> Bool { if let to = to as? AutomaticMediaDownloadSettings { return self == to } else { return false } } } public func currentAutomaticMediaDownloadSettings(postbox: Postbox) -> Signal { return postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings]) |> map { view -> AutomaticMediaDownloadSettings in let automaticMediaDownloadSettings: AutomaticMediaDownloadSettings if let value = view.values[ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings] as? AutomaticMediaDownloadSettings { automaticMediaDownloadSettings = value } else { automaticMediaDownloadSettings = AutomaticMediaDownloadSettings.defaultSettings } return automaticMediaDownloadSettings } } func updateMediaDownloadSettingsInteractively(postbox: Postbox, _ f: @escaping (AutomaticMediaDownloadSettings) -> AutomaticMediaDownloadSettings) -> Signal { return postbox.modify { modifier -> Void in modifier.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings, { entry in let currentSettings: AutomaticMediaDownloadSettings if let entry = entry as? AutomaticMediaDownloadSettings { currentSettings = entry } else { currentSettings = AutomaticMediaDownloadSettings.defaultSettings } let updated = f(currentSettings) return updated }) } } private func categoriesForPeer(_ peer: Peer, settings: AutomaticMediaDownloadSettings) -> AutomaticMediaDownloadCategories { if let _ = peer as? TelegramUser { return settings.peers.contacts } else if let _ = peer as? TelegramSecretChat { return settings.peers.contacts } else if let channel = peer as? TelegramChannel { if case .broadcast = channel.info { return settings.peers.channels } else { return settings.peers.groups } } else { return settings.peers.channels } } private func categoryForPeerAndMedia(settings: AutomaticMediaDownloadSettings, peer: Peer, media: Media) -> (AutomaticMediaDownloadCategory, Int32?)? { let categories = categoriesForPeer(peer, settings: settings) if let _ = media as? TelegramMediaImage { return (categories.photo, nil) } else if let file = media as? TelegramMediaFile { for attribute in file.attributes { switch attribute { case let .Video(_, _, flags): if flags.contains(.instantRoundVideo) { return (categories.videoMessage, file.size.flatMap(Int32.init)) } else { return (categories.video, file.size.flatMap(Int32.init)) } case let .Audio(isVoice, _, _, _, _): if isVoice { return (categories.voiceMessage, file.size.flatMap(Int32.init)) } default: break } } return (categories.file, file.size.flatMap(Int32.init)) } else { return nil } } public func shouldDownloadMediaAutomatically(settings: AutomaticMediaDownloadSettings, peer: Peer?, media: Media) -> Bool { if !settings.masterEnabled { return false } guard let peer = peer else { return false } if let (category, size) = categoryForPeerAndMedia(settings: settings, peer: peer, media: media) { if let size = size { return category.cellular && size <= category.sizeLimit } else if category.sizeLimit == Int32.max { return category.cellular } else { return false } } else { return false } }