mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Low power experiments
This commit is contained in:
parent
e7fdd14b23
commit
bb7ffa6191
24 changed files with 543 additions and 115 deletions
|
|
@ -718,7 +718,12 @@ private final class NotificationServiceHandler {
|
|||
|
||||
let _ = (combineLatest(queue: self.queue,
|
||||
self.accountManager.accountRecords(),
|
||||
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings, SharedDataKeys.loggingSettings])
|
||||
self.accountManager.sharedData(keys: [
|
||||
ApplicationSpecificSharedDataKeys.inAppNotificationSettings,
|
||||
ApplicationSpecificSharedDataKeys.voiceCallSettings,
|
||||
ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings,
|
||||
SharedDataKeys.loggingSettings
|
||||
])
|
||||
)
|
||||
|> take(1)
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in
|
||||
|
|
@ -728,6 +733,14 @@ private final class NotificationServiceHandler {
|
|||
let loggingSettings = sharedData.entries[SharedDataKeys.loggingSettings]?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings
|
||||
Logger.shared.logToFile = loggingSettings.logToFile
|
||||
Logger.shared.logToConsole = loggingSettings.logToConsole
|
||||
|
||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]?.get(MediaAutoDownloadSettings.self) {
|
||||
automaticMediaDownloadSettings = value
|
||||
} else {
|
||||
automaticMediaDownloadSettings = MediaAutoDownloadSettings.defaultSettings
|
||||
}
|
||||
let shouldSynchronizeState = automaticMediaDownloadSettings.energyUsageSettings.synchronizeInBackground
|
||||
|
||||
if let keyId = notificationPayloadKeyId(data: payloadData) {
|
||||
outer: for listRecord in records.records {
|
||||
|
|
@ -1406,34 +1419,38 @@ private final class NotificationServiceHandler {
|
|||
|
||||
let pollSignal: Signal<Never, NoError>
|
||||
|
||||
stateManager.network.shouldKeepConnection.set(.single(true))
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will poll channel \(peerId)")
|
||||
|
||||
pollSignal = standalonePollChannelOnce(
|
||||
accountPeerId: stateManager.accountPeerId,
|
||||
postbox: stateManager.postbox,
|
||||
network: stateManager.network,
|
||||
peerId: peerId,
|
||||
stateManager: stateManager
|
||||
)
|
||||
if !shouldSynchronizeState {
|
||||
pollSignal = .complete()
|
||||
} else {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference")
|
||||
enum ControlError {
|
||||
case restart
|
||||
}
|
||||
let signal = stateManager.standalonePollDifference()
|
||||
|> castError(ControlError.self)
|
||||
|> mapToSignal { result -> Signal<Never, ControlError> in
|
||||
if result {
|
||||
return .complete()
|
||||
} else {
|
||||
return .fail(.restart)
|
||||
stateManager.network.shouldKeepConnection.set(.single(true))
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will poll channel \(peerId)")
|
||||
|
||||
pollSignal = standalonePollChannelOnce(
|
||||
accountPeerId: stateManager.accountPeerId,
|
||||
postbox: stateManager.postbox,
|
||||
network: stateManager.network,
|
||||
peerId: peerId,
|
||||
stateManager: stateManager
|
||||
)
|
||||
} else {
|
||||
Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference")
|
||||
enum ControlError {
|
||||
case restart
|
||||
}
|
||||
let signal = stateManager.standalonePollDifference()
|
||||
|> castError(ControlError.self)
|
||||
|> mapToSignal { result -> Signal<Never, ControlError> in
|
||||
if result {
|
||||
return .complete()
|
||||
} else {
|
||||
return .fail(.restart)
|
||||
}
|
||||
}
|
||||
|> restartIfError
|
||||
|
||||
pollSignal = signal
|
||||
}
|
||||
|> restartIfError
|
||||
|
||||
pollSignal = signal
|
||||
}
|
||||
|
||||
let pollWithUpdatedContent: Signal<NotificationContent, NoError>
|
||||
|
|
|
|||
|
|
@ -751,6 +751,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||
var currentInAppNotificationSettings: Atomic<InAppNotificationSettings> { get }
|
||||
var currentMediaInputSettings: Atomic<MediaInputSettings> { get }
|
||||
|
||||
var energyUsageSettings: EnergyUsageSettings { get }
|
||||
|
||||
var applicationBindings: TelegramApplicationBindings { get }
|
||||
|
||||
var authorizationPushConfiguration: Signal<AuthorizationCodePushNotificationConfiguration?, NoError> { get }
|
||||
|
|
|
|||
|
|
@ -1754,12 +1754,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
|
||||
Queue.mainQueue().after(1.0, {
|
||||
let _ = (
|
||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.forcedPasswordSetupKey()))
|
||||
|> map { entry -> Int32? in
|
||||
return entry?.get(ApplicationSpecificCounterNotice.self)?.value
|
||||
self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId),
|
||||
TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.forcedPasswordSetupKey())
|
||||
)
|
||||
|> map { peer, entry -> (phoneNumber: String?, nortice: Int32?) in
|
||||
var phoneNumber: String?
|
||||
if case let .user(user) = peer {
|
||||
phoneNumber = user.phone
|
||||
}
|
||||
return (phoneNumber, entry?.get(ApplicationSpecificCounterNotice.self)?.value)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
).start(next: { [weak self] value in
|
||||
).start(next: { [weak self] phoneNumber, value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -1772,7 +1779,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_Title,
|
||||
text: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_Text,
|
||||
actionText: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_Action,
|
||||
doneText: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DoneAction
|
||||
doneText: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DoneAction,
|
||||
phoneNumber: phoneNumber
|
||||
)))
|
||||
controller.dismissConfirmation = { [weak controller] f in
|
||||
guard let strongSelf = self, let controller = controller else {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import SearchUI
|
|||
import ContextUI
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import TelegramUIPreferences
|
||||
|
||||
enum ChatListContainerNodeFilter: Equatable {
|
||||
case all
|
||||
|
|
@ -647,13 +648,26 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
return (state, filterId)
|
||||
})
|
||||
|
||||
let enablePreload = context.sharedContext.accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]))
|
||||
|> map { sharedData -> Bool in
|
||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]?.get(MediaAutoDownloadSettings.self) {
|
||||
automaticMediaDownloadSettings = value
|
||||
} else {
|
||||
automaticMediaDownloadSettings = MediaAutoDownloadSettings.defaultSettings
|
||||
}
|
||||
return automaticMediaDownloadSettings.energyUsageSettings.autodownloadInBackground
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
if self.controlsHistoryPreload, case .chatList(groupId: .root) = self.location {
|
||||
self.context.account.viewTracker.chatListPreloadItems.set(combineLatest(queue: .mainQueue(),
|
||||
context.sharedContext.hasOngoingCall.get(),
|
||||
itemNode.listNode.preloadItems.get()
|
||||
itemNode.listNode.preloadItems.get(),
|
||||
enablePreload
|
||||
)
|
||||
|> map { hasOngoingCall, preloadItems -> Set<ChatHistoryPreloadItem> in
|
||||
if hasOngoingCall {
|
||||
|> map { hasOngoingCall, preloadItems, enablePreload -> Set<ChatHistoryPreloadItem> in
|
||||
if hasOngoingCall || !enablePreload {
|
||||
return Set()
|
||||
} else {
|
||||
return Set(preloadItems)
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: titleTopicIconContent,
|
||||
isVisibleForAnimations: currentNode?.visibilityStatus ?? false,
|
||||
isVisibleForAnimations: (currentNode?.visibilityStatus ?? false) && context.sharedContext.energyUsageSettings.loopEmoji,
|
||||
action: nil
|
||||
)
|
||||
|
||||
|
|
@ -2788,7 +2788,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
animationCache: item.interaction.animationCache,
|
||||
animationRenderer: item.interaction.animationRenderer,
|
||||
content: avatarIconContent,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus && item.context.sharedContext.energyUsageSettings.loopEmoji,
|
||||
action: nil
|
||||
)
|
||||
strongSelf.avatarIconComponent = avatarIconComponent
|
||||
|
|
@ -3285,7 +3285,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
animationCache: item.interaction.animationCache,
|
||||
animationRenderer: item.interaction.animationRenderer,
|
||||
content: currentCredibilityIconContent,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus && item.context.sharedContext.energyUsageSettings.loopEmoji,
|
||||
action: nil
|
||||
)
|
||||
strongSelf.credibilityIconComponent = credibilityIconComponent
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public final class ReactionIconView: PortalSourceView {
|
|||
|
||||
animationLayer.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
|
||||
animationLayer.isVisibleForAnimations = animateIdle
|
||||
animationLayer.isVisibleForAnimations = animateIdle && context.sharedContext.energyUsageSettings.loopEmoji
|
||||
}
|
||||
|
||||
func reset() {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public enum TwoFactorDataInputMode {
|
|||
case authorized
|
||||
}
|
||||
|
||||
case password(doneText: String)
|
||||
case password(phoneNumber: String?, doneText: String)
|
||||
case passwordRecoveryEmail(emailPattern: String, mode: PasswordRecoveryEmailMode, doneText: String)
|
||||
case passwordRecovery(recovery: Recovery, doneText: String)
|
||||
case emailAddress(password: String, hint: String, doneText: String)
|
||||
|
|
@ -99,8 +99,11 @@ public final class TwoFactorDataInputScreen: ViewController {
|
|||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if case .rememberPassword = self.mode {
|
||||
switch self.mode {
|
||||
case .rememberPassword, .password:
|
||||
(self.displayNode as? TwoFactorDataInputScreenNode)?.focus()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +113,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
|||
return
|
||||
}
|
||||
switch strongSelf.mode {
|
||||
case let .password(doneText):
|
||||
case let .password(_, doneText):
|
||||
let values = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText
|
||||
if values.count != 2 {
|
||||
return
|
||||
|
|
@ -924,7 +927,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
|||
}
|
||||
|
||||
private enum TwoFactorDataInputTextNodeType {
|
||||
case password(confirmation: Bool)
|
||||
case password(phoneNumber: String?, confirmation: Bool)
|
||||
case email
|
||||
case code
|
||||
case hint
|
||||
|
|
@ -984,6 +987,7 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
|||
private let toggleTextHidden: (TwoFactorDataInputTextNode) -> Void
|
||||
|
||||
private let backgroundNode: ASImageNode
|
||||
private var shadowInputNode: TextFieldNode?
|
||||
private let inputNode: TextFieldNode
|
||||
private let hideButtonNode: HighlightableButtonNode
|
||||
private let clearButtonNode: HighlightableButtonNode
|
||||
|
|
@ -1068,7 +1072,7 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
|||
self.hideButtonNode = HighlightableButtonNode()
|
||||
|
||||
switch mode {
|
||||
case let .password(confirmation):
|
||||
case let .password(phoneNumber, confirmation):
|
||||
self.inputNode.textField.keyboardType = .default
|
||||
self.inputNode.textField.isSecureTextEntry = true
|
||||
if confirmation {
|
||||
|
|
@ -1076,29 +1080,68 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
|||
} else {
|
||||
self.inputNode.textField.returnKeyType = .next
|
||||
}
|
||||
if #available(iOS 12.0, *) {
|
||||
if !confirmation, let phoneNumber {
|
||||
let shadowInputNode = TextFieldNode()
|
||||
shadowInputNode.textField.font = Font.regular(17.0)
|
||||
shadowInputNode.textField.textColor = theme.list.freePlainInputField.primaryColor
|
||||
shadowInputNode.textField.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: theme.list.freePlainInputField.placeholderColor)
|
||||
self.shadowInputNode = shadowInputNode
|
||||
|
||||
|
||||
shadowInputNode.textField.textContentType = .username
|
||||
shadowInputNode.textField.text = phoneNumber
|
||||
}
|
||||
|
||||
self.inputNode.textField.textContentType = .newPassword
|
||||
self.inputNode.textField.passwordRules = UITextInputPasswordRules(descriptor: "minlength: 8;")
|
||||
}
|
||||
self.hideButtonNode.isHidden = confirmation
|
||||
case .email:
|
||||
self.inputNode.textField.keyboardType = .emailAddress
|
||||
self.inputNode.textField.returnKeyType = .done
|
||||
self.hideButtonNode.isHidden = true
|
||||
|
||||
if #available(iOS 12.0, *) {
|
||||
self.inputNode.textField.textContentType = .emailAddress
|
||||
}
|
||||
|
||||
self.inputNode.textField.autocorrectionType = .no
|
||||
self.inputNode.textField.autocapitalizationType = .none
|
||||
self.inputNode.textField.spellCheckingType = .no
|
||||
if #available(iOS 11.0, *) {
|
||||
self.inputNode.textField.smartQuotesType = .no
|
||||
self.inputNode.textField.smartDashesType = .no
|
||||
self.inputNode.textField.smartInsertDeleteType = .no
|
||||
}
|
||||
case .code:
|
||||
self.inputNode.textField.keyboardType = .numberPad
|
||||
self.inputNode.textField.returnKeyType = .done
|
||||
self.hideButtonNode.isHidden = true
|
||||
|
||||
self.inputNode.textField.autocorrectionType = .no
|
||||
self.inputNode.textField.autocapitalizationType = .none
|
||||
self.inputNode.textField.spellCheckingType = .no
|
||||
if #available(iOS 11.0, *) {
|
||||
self.inputNode.textField.smartQuotesType = .no
|
||||
self.inputNode.textField.smartDashesType = .no
|
||||
self.inputNode.textField.smartInsertDeleteType = .no
|
||||
}
|
||||
case .hint:
|
||||
self.inputNode.textField.keyboardType = .asciiCapable
|
||||
self.inputNode.textField.returnKeyType = .done
|
||||
self.hideButtonNode.isHidden = true
|
||||
|
||||
self.inputNode.textField.autocorrectionType = .no
|
||||
self.inputNode.textField.autocapitalizationType = .none
|
||||
self.inputNode.textField.spellCheckingType = .no
|
||||
if #available(iOS 11.0, *) {
|
||||
self.inputNode.textField.smartQuotesType = .no
|
||||
self.inputNode.textField.smartDashesType = .no
|
||||
self.inputNode.textField.smartInsertDeleteType = .no
|
||||
}
|
||||
}
|
||||
|
||||
self.inputNode.textField.autocorrectionType = .no
|
||||
self.inputNode.textField.autocapitalizationType = .none
|
||||
self.inputNode.textField.spellCheckingType = .no
|
||||
if #available(iOS 11.0, *) {
|
||||
self.inputNode.textField.smartQuotesType = .no
|
||||
self.inputNode.textField.smartDashesType = .no
|
||||
self.inputNode.textField.smartInsertDeleteType = .no
|
||||
}
|
||||
self.inputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
|
||||
|
||||
self.hideButtonNode.setImage(generateTextHiddenImage(color: theme.list.freePlainInputField.controlColor, on: false), for: [])
|
||||
|
|
@ -1110,6 +1153,10 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
|||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
if let shadowInputNode = self.shadowInputNode {
|
||||
shadowInputNode.alpha = 0.001
|
||||
self.addSubnode(shadowInputNode)
|
||||
}
|
||||
self.addSubnode(self.inputNode)
|
||||
self.addSubnode(self.hideButtonNode)
|
||||
|
||||
|
|
@ -1179,6 +1226,10 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
|||
let leftInset: CGFloat = 16.0
|
||||
let rightInset: CGFloat = 38.0
|
||||
|
||||
if let shadowInputNode = self.shadowInputNode {
|
||||
transition.updateFrame(node: shadowInputNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: size.width - leftInset - rightInset, height: size.height)))
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.inputNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: size.width - leftInset - rightInset, height: size.height)))
|
||||
transition.updateFrame(node: self.hideButtonNode, frame: CGRect(origin: CGPoint(x: size.width - rightInset - 4.0, y: 0.0), size: CGSize(width: rightInset + 4.0, height: size.height)))
|
||||
|
|
@ -1299,7 +1350,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
var toggleTextHidden: ((TwoFactorDataInputTextNode) -> Void)?
|
||||
|
||||
switch mode {
|
||||
case .password:
|
||||
case let .password(phoneNumber, _):
|
||||
title = presentationData.strings.TwoFactorSetup_Password_Title
|
||||
text = NSAttributedString(string: "", font: Font.regular(16.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
buttonText = presentationData.strings.TwoFactorSetup_Password_Action
|
||||
|
|
@ -1307,7 +1358,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
changeEmailActionText = ""
|
||||
resendCodeActionText = ""
|
||||
inputNodes = [
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(confirmation: false), placeholder: presentationData.strings.TwoFactorSetup_Password_PlaceholderPassword, focusUpdated: { node, focused in
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(phoneNumber: phoneNumber, confirmation: false), placeholder: presentationData.strings.TwoFactorSetup_Password_PlaceholderPassword, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
|
|
@ -1316,7 +1367,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
}, toggleTextHidden: { node in
|
||||
toggleTextHidden?(node)
|
||||
}),
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(confirmation: true), placeholder: presentationData.strings.TwoFactorSetup_Password_PlaceholderConfirmPassword, focusUpdated: { node, focused in
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(phoneNumber: phoneNumber, confirmation: true), placeholder: presentationData.strings.TwoFactorSetup_Password_PlaceholderConfirmPassword, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
|
|
@ -1334,7 +1385,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
changeEmailActionText = ""
|
||||
resendCodeActionText = ""
|
||||
inputNodes = [
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(confirmation: false), placeholder: presentationData.strings.TwoFactorSetup_PasswordRecovery_PlaceholderPassword, focusUpdated: { node, focused in
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(phoneNumber: nil, confirmation: false), placeholder: presentationData.strings.TwoFactorSetup_PasswordRecovery_PlaceholderPassword, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
|
|
@ -1343,7 +1394,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
}, toggleTextHidden: { node in
|
||||
toggleTextHidden?(node)
|
||||
}),
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(confirmation: true), placeholder: presentationData.strings.TwoFactorSetup_PasswordRecovery_PlaceholderConfirmPassword, focusUpdated: { node, focused in
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(phoneNumber: nil, confirmation: true), placeholder: presentationData.strings.TwoFactorSetup_PasswordRecovery_PlaceholderConfirmPassword, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
|
|
@ -1453,7 +1504,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
|||
changeEmailActionText = ""
|
||||
resendCodeActionText = ""
|
||||
inputNodes = [
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(confirmation: false), placeholder: presentationData.strings.TwoFactorRemember_Placeholder, focusUpdated: { node, focused in
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .password(phoneNumber: nil, confirmation: false), placeholder: presentationData.strings.TwoFactorRemember_Placeholder, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
|
|
|
|||
|
|
@ -18,17 +18,20 @@ public enum TwoFactorAuthSplashMode {
|
|||
public var text: String
|
||||
public var actionText: String
|
||||
public var doneText: String
|
||||
public var phoneNumber: String?
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
text: String,
|
||||
actionText: String,
|
||||
doneText: String
|
||||
doneText: String,
|
||||
phoneNumber: String?
|
||||
) {
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.actionText = actionText
|
||||
self.doneText = doneText
|
||||
self.phoneNumber = phoneNumber
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +103,7 @@ public final class TwoFactorAuthSplashScreen: ViewController {
|
|||
}
|
||||
switch strongSelf.mode {
|
||||
case let .intro(intro):
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .password(doneText: intro.doneText), stateUpdated: { _ in
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .password(phoneNumber: intro.phoneNumber, doneText: intro.doneText), stateUpdated: { _ in
|
||||
}, presentation: strongSelf.navigationPresentation))
|
||||
case .done, .remember:
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
|
|
|
|||
|
|
@ -735,9 +735,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
loopIdle = true
|
||||
#endif
|
||||
if !self.context.sharedContext.energyUsageSettings.loopEmoji {
|
||||
loopIdle = false
|
||||
}
|
||||
|
||||
var validIndices = Set<Int>()
|
||||
var nextX: CGFloat = sideInset
|
||||
|
|
|
|||
|
|
@ -33,12 +33,13 @@ private final class DataAndStorageControllerArguments {
|
|||
let toggleRaiseToListen: (Bool) -> Void
|
||||
let toggleAutoplayGifs: (Bool) -> Void
|
||||
let toggleAutoplayVideos: (Bool) -> Void
|
||||
let openEnergySavingSettings: () -> Void
|
||||
let toggleDownloadInBackground: (Bool) -> Void
|
||||
let openBrowserSelection: () -> Void
|
||||
let openIntents: () -> Void
|
||||
let toggleEnableSensitiveContent: (Bool) -> Void
|
||||
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, openEnergySavingSettings: @escaping () -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) {
|
||||
self.openStorageUsage = openStorageUsage
|
||||
self.openNetworkUsage = openNetworkUsage
|
||||
self.openProxy = openProxy
|
||||
|
|
@ -51,6 +52,7 @@ private final class DataAndStorageControllerArguments {
|
|||
self.toggleRaiseToListen = toggleRaiseToListen
|
||||
self.toggleAutoplayGifs = toggleAutoplayGifs
|
||||
self.toggleAutoplayVideos = toggleAutoplayVideos
|
||||
self.openEnergySavingSettings = openEnergySavingSettings
|
||||
self.toggleDownloadInBackground = toggleDownloadInBackground
|
||||
self.openBrowserSelection = openBrowserSelection
|
||||
self.openIntents = openIntents
|
||||
|
|
@ -64,6 +66,7 @@ private enum DataAndStorageSection: Int32 {
|
|||
case autoSave
|
||||
case backgroundDownload
|
||||
case autoPlay
|
||||
case energySaving
|
||||
case voiceCalls
|
||||
case other
|
||||
case connection
|
||||
|
|
@ -107,6 +110,9 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||
case autoplayHeader(PresentationTheme, String)
|
||||
case autoplayGifs(PresentationTheme, String, Bool)
|
||||
case autoplayVideos(PresentationTheme, String, Bool)
|
||||
|
||||
case energySaving
|
||||
|
||||
case useLessVoiceData(PresentationTheme, String, Bool)
|
||||
case useLessVoiceDataInfo(PresentationTheme, String)
|
||||
case otherHeader(PresentationTheme, String)
|
||||
|
|
@ -135,6 +141,8 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||
return DataAndStorageSection.voiceCalls.rawValue
|
||||
case .autoplayHeader, .autoplayGifs, .autoplayVideos:
|
||||
return DataAndStorageSection.autoPlay.rawValue
|
||||
case .energySaving:
|
||||
return DataAndStorageSection.energySaving.rawValue
|
||||
case .otherHeader, .shareSheet, .saveEditedPhotos, .openLinksIn, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo:
|
||||
return DataAndStorageSection.other.rawValue
|
||||
case .connectionHeader, .connectionProxy:
|
||||
|
|
@ -178,10 +186,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||
return 26
|
||||
case .autoplayVideos:
|
||||
return 27
|
||||
case .otherHeader:
|
||||
case .energySaving:
|
||||
return 28
|
||||
case .shareSheet:
|
||||
case .otherHeader:
|
||||
return 29
|
||||
case .shareSheet:
|
||||
return 30
|
||||
case .saveEditedPhotos:
|
||||
return 31
|
||||
case .openLinksIn:
|
||||
|
|
@ -275,6 +285,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case .energySaving:
|
||||
if case .energySaving = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .useLessVoiceData(lhsTheme, lhsText, lhsValue):
|
||||
if case let .useLessVoiceData(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
|
|
@ -424,6 +440,11 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
|
|||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleAutoplayVideos(value)
|
||||
}, tag: DataAndStorageEntryTag.autoplayVideos)
|
||||
case .energySaving:
|
||||
//TODO:localize
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Energy Settings", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openEnergySavingSettings()
|
||||
})
|
||||
case let .useLessVoiceData(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleVoiceUseLessData(value)
|
||||
|
|
@ -667,6 +688,8 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
|||
entries.append(.autoplayGifs(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayGifs, data.automaticMediaDownloadSettings.autoplayGifs))
|
||||
entries.append(.autoplayVideos(presentationData.theme, presentationData.strings.ChatSettings_AutoPlayVideos, data.automaticMediaDownloadSettings.autoplayVideos))
|
||||
|
||||
entries.append(.energySaving)
|
||||
|
||||
entries.append(.otherHeader(presentationData.theme, presentationData.strings.ChatSettings_Other))
|
||||
if #available(iOSApplicationExtension 13.2, iOS 13.2, *) {
|
||||
entries.append(.shareSheet(presentationData.theme, presentationData.strings.ChatSettings_IntentsSettings))
|
||||
|
|
@ -922,6 +945,8 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
|
|||
settings.autoplayVideos = value
|
||||
return settings
|
||||
}).start()
|
||||
}, openEnergySavingSettings: {
|
||||
pushControllerImpl?(energySavingSettingsScreen(context: context))
|
||||
}, toggleDownloadInBackground: { value in
|
||||
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
|
|
|
|||
|
|
@ -0,0 +1,199 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
|
||||
enum ItemType: CaseIterable {
|
||||
case loopEmoji
|
||||
case playVideoAvatars
|
||||
case fullTranslucency
|
||||
case extendBackgroundWork
|
||||
case synchronizeInBackground
|
||||
case autodownloadInBackground
|
||||
|
||||
var settingsKeyPath: WritableKeyPath<EnergyUsageSettings, Bool> {
|
||||
switch self {
|
||||
case .loopEmoji:
|
||||
return \.loopEmoji
|
||||
case .playVideoAvatars:
|
||||
return \.playVideoAvatars
|
||||
case .fullTranslucency:
|
||||
return \.fullTranslucency
|
||||
case .extendBackgroundWork:
|
||||
return \.extendBackgroundWork
|
||||
case .synchronizeInBackground:
|
||||
return \.synchronizeInBackground
|
||||
case .autodownloadInBackground:
|
||||
return \.autodownloadInBackground
|
||||
}
|
||||
}
|
||||
|
||||
func title(strings: PresentationStrings) -> String {
|
||||
//TODO:localize
|
||||
switch self {
|
||||
case .loopEmoji:
|
||||
return "Loop Animated Emoji"
|
||||
case .playVideoAvatars:
|
||||
return "Play Video Avatars"
|
||||
case .fullTranslucency:
|
||||
return "Translucency Effects"
|
||||
case .extendBackgroundWork:
|
||||
return "Extended Background Time"
|
||||
case .synchronizeInBackground:
|
||||
return "Background Sync"
|
||||
case .autodownloadInBackground:
|
||||
return "Preload Media in Chats"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class EnergeSavingSettingsScreenArguments {
|
||||
let toggleAll: (Bool) -> Void
|
||||
let toggleItem: (ItemType) -> Void
|
||||
|
||||
init(toggleAll: @escaping (Bool) -> Void, toggleItem: @escaping (ItemType) -> Void) {
|
||||
self.toggleAll = toggleAll
|
||||
self.toggleItem = toggleItem
|
||||
}
|
||||
}
|
||||
|
||||
private enum EnergeSavingSettingsScreenSection: Int32 {
|
||||
case all
|
||||
case items
|
||||
}
|
||||
|
||||
private enum EnergeSavingSettingsScreenEntry: ItemListNodeEntry {
|
||||
enum StableId: Hashable {
|
||||
case all
|
||||
case item(ItemType)
|
||||
}
|
||||
|
||||
case all(Bool)
|
||||
case item(index: Int, type: ItemType, value: Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .all:
|
||||
return EnergeSavingSettingsScreenSection.all.rawValue
|
||||
case .item:
|
||||
return EnergeSavingSettingsScreenSection.items.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var sortIndex: Int {
|
||||
switch self {
|
||||
case .all:
|
||||
return -1
|
||||
case let .item(index, _, _):
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: StableId {
|
||||
switch self {
|
||||
case .all:
|
||||
return .all
|
||||
case let .item(_, type, _):
|
||||
return .item(type)
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: EnergeSavingSettingsScreenEntry, rhs: EnergeSavingSettingsScreenEntry) -> Bool {
|
||||
return lhs.sortIndex < rhs.sortIndex
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! EnergeSavingSettingsScreenArguments
|
||||
switch self {
|
||||
case let .all(value):
|
||||
//TODO:localize
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Enable All", value: value, enableInteractiveChanges: true, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleAll(value)
|
||||
})
|
||||
case let .item(_, type, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: type.title(strings: presentationData.strings), value: value, enableInteractiveChanges: true, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleItem(type)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func energeSavingSettingsScreenEntries(
|
||||
presentationData: PresentationData,
|
||||
settings: MediaAutoDownloadSettings
|
||||
) -> [EnergeSavingSettingsScreenEntry] {
|
||||
var entries: [EnergeSavingSettingsScreenEntry] = []
|
||||
|
||||
entries.append(.all(ItemType.allCases.allSatisfy({ item in settings.energyUsageSettings[keyPath: item.settingsKeyPath] })))
|
||||
|
||||
for type in ItemType.allCases {
|
||||
entries.append(.item(index: entries.count, type: type, value: settings.energyUsageSettings[keyPath: type.settingsKeyPath]))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func energySavingSettingsScreen(context: AccountContext) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
let _ = pushControllerImpl
|
||||
|
||||
let arguments = EnergeSavingSettingsScreenArguments(
|
||||
toggleAll: { value in
|
||||
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
for type in ItemType.allCases {
|
||||
settings.energyUsageSettings[keyPath: type.settingsKeyPath] = value
|
||||
}
|
||||
return settings
|
||||
}).start()
|
||||
},
|
||||
toggleItem: { type in
|
||||
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
settings.energyUsageSettings[keyPath: type.settingsKeyPath] = !settings.energyUsageSettings[keyPath: type.settingsKeyPath]
|
||||
return settings
|
||||
}).start()
|
||||
}
|
||||
)
|
||||
|
||||
let signal = combineLatest(
|
||||
context.sharedContext.presentationData,
|
||||
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]))
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]?.get(MediaAutoDownloadSettings.self) {
|
||||
automaticMediaDownloadSettings = value
|
||||
} else {
|
||||
automaticMediaDownloadSettings = MediaAutoDownloadSettings.defaultSettings
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let controllerState = ItemListControllerState(
|
||||
presentationData: ItemListPresentationData(presentationData),
|
||||
title: .text("Energy Saving"),
|
||||
leftNavigationButton: nil,
|
||||
rightNavigationButton: nil,
|
||||
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back),
|
||||
animateChanges: false
|
||||
)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: energeSavingSettingsScreenEntries(presentationData: presentationData, settings: automaticMediaDownloadSettings), style: .blocks, emptyStateItem: nil, animateChanges: true)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
if let controller = controller {
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
@ -248,7 +248,8 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo
|
|||
title: presentationData.strings.TwoFactorSetup_Intro_Title,
|
||||
text: presentationData.strings.TwoFactorSetup_Intro_Text,
|
||||
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action,
|
||||
phoneNumber: nil
|
||||
)))
|
||||
|
||||
replaceTopControllerImpl?(controller, false)
|
||||
|
|
|
|||
|
|
@ -944,28 +944,37 @@ public func privacyAndSecurityController(
|
|||
}
|
||||
})
|
||||
}, openTwoStepVerification: { data in
|
||||
if let data = data {
|
||||
switch data {
|
||||
case .set:
|
||||
break
|
||||
case let .notSet(pendingEmail):
|
||||
if pendingEmail == nil {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = TwoFactorAuthSplashScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .intro(.init(
|
||||
title: presentationData.strings.TwoFactorSetup_Intro_Title,
|
||||
text: presentationData.strings.TwoFactorSetup_Intro_Text,
|
||||
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action
|
||||
)))
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let data = data {
|
||||
switch data {
|
||||
case .set:
|
||||
break
|
||||
case let .notSet(pendingEmail):
|
||||
if pendingEmail == nil {
|
||||
var phoneNumber: String?
|
||||
if case let .user(user) = peer {
|
||||
phoneNumber = user.phone
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = TwoFactorAuthSplashScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .intro(.init(
|
||||
title: presentationData.strings.TwoFactorSetup_Intro_Title,
|
||||
text: presentationData.strings.TwoFactorSetup_Intro_Text,
|
||||
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action,
|
||||
phoneNumber: phoneNumber
|
||||
)))
|
||||
|
||||
pushControllerImpl?(controller, true)
|
||||
return
|
||||
pushControllerImpl?(controller, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let controller = twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: data.flatMap({ Signal<TwoStepVerificationUnlockSettingsControllerData, NoError>.single(.access(configuration: $0)) })))
|
||||
pushControllerImpl?(controller, true)
|
||||
let controller = twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: data.flatMap({ Signal<TwoStepVerificationUnlockSettingsControllerData, NoError>.single(.access(configuration: $0)) })))
|
||||
pushControllerImpl?(controller, true)
|
||||
})
|
||||
}, openActiveSessions: {
|
||||
pushControllerImpl?(recentSessionsController(context: context, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, websitesOnly: true), true)
|
||||
}, toggleArchiveAndMuteNonContacts: { archiveValue in
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ public func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewCon
|
|||
title: presentationData.strings.TwoFactorSetup_Intro_Title,
|
||||
text: presentationData.strings.TwoFactorSetup_Intro_Text,
|
||||
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action
|
||||
doneText: presentationData.strings.TwoFactorSetup_Done_Action,
|
||||
phoneNumber: nil
|
||||
)))
|
||||
return controller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,21 @@ func addSynchronizeRecentlyUsedMediaOperation(transaction: Transaction, category
|
|||
}
|
||||
let peerId = PeerId(0)
|
||||
|
||||
var topOperation: (SynchronizeRecentlyUsedMediaOperation, Int32)?
|
||||
var removeOperations: [(SynchronizeRecentlyUsedMediaOperation, Int32)] = []
|
||||
transaction.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in
|
||||
if let operation = entry.contents as? SynchronizeRecentlyUsedMediaOperation {
|
||||
topOperation = (operation, entry.tagLocalIndex)
|
||||
if case .sync = operation.content {
|
||||
removeOperations.append((operation, entry.tagLocalIndex))
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if let (topOperation, topLocalIndex) = topOperation, case .sync = topOperation.content {
|
||||
for (_, topLocalIndex) in removeOperations {
|
||||
let _ = transaction.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: topLocalIndex)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1342,7 +1342,7 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager
|
|||
let itemFrame = itemLayout.frame(at: index)
|
||||
itemLayer.frame = itemFrame
|
||||
|
||||
itemLayer.isVisibleForAnimations = true
|
||||
itemLayer.isVisibleForAnimations = context.sharedContext.energyUsageSettings.loopEmoji
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2160,7 +2160,7 @@ private final class EmptySearchResultsView: UIView {
|
|||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: .animation(content: .file(file: file), size: CGSize(width: 32.0, height: 32.0), placeholderColor: titleColor, themeColor: nil, loopMode: .forever),
|
||||
isVisibleForAnimations: true,
|
||||
isVisibleForAnimations: context.sharedContext.energyUsageSettings.loopEmoji,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
|
|
@ -5795,7 +5795,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||
placeholderView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
}
|
||||
|
||||
itemLayer.isVisibleForAnimations = keyboardChildEnvironment.isContentInFocus
|
||||
itemLayer.isVisibleForAnimations = keyboardChildEnvironment.isContentInFocus && component.context.sharedContext.energyUsageSettings.loopEmoji
|
||||
}
|
||||
}
|
||||
if itemGroup.fillWithLoadingPlaceholders {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||
itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor
|
||||
}
|
||||
|
||||
itemLayer.isVisibleForAnimations = itemEnvironment.isContentInFocus
|
||||
itemLayer.isVisibleForAnimations = itemEnvironment.isContentInFocus && component.context.sharedContext.energyUsageSettings.loopEmoji
|
||||
}
|
||||
|
||||
if itemEnvironment.isExpanded {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ public final class TextNodeWithEntities {
|
|||
public let textNode: TextNode
|
||||
private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:]
|
||||
|
||||
private var enableLooping: Bool = true
|
||||
|
||||
public var visibilityRect: CGRect? {
|
||||
didSet {
|
||||
if !self.inlineStickerItemLayers.isEmpty && oldValue != self.visibilityRect {
|
||||
|
|
@ -107,7 +109,7 @@ public final class TextNodeWithEntities {
|
|||
} else {
|
||||
isItemVisible = false
|
||||
}
|
||||
itemLayer.isVisibleForAnimations = isItemVisible
|
||||
itemLayer.isVisibleForAnimations = self.enableLooping && isItemVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +220,8 @@ public final class TextNodeWithEntities {
|
|||
}
|
||||
|
||||
private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor, attemptSynchronousLoad: Bool) {
|
||||
self.enableLooping = context.sharedContext.energyUsageSettings.loopEmoji
|
||||
|
||||
var nextIndexById: [Int64: Int] = [:]
|
||||
var validIds: [InlineStickerItemLayer.Key] = []
|
||||
|
||||
|
|
@ -250,7 +254,7 @@ public final class TextNodeWithEntities {
|
|||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.textNode.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = self.isItemVisible(itemRect: itemFrame)
|
||||
itemLayer.isVisibleForAnimations = self.enableLooping && self.isItemVisible(itemRect: itemFrame)
|
||||
}
|
||||
|
||||
itemLayer.frame = itemFrame
|
||||
|
|
@ -284,6 +288,8 @@ public class ImmediateTextNodeWithEntities: TextNode {
|
|||
public var cutout: TextNodeCutout?
|
||||
public var displaySpoilers = false
|
||||
|
||||
private var enableLooping: Bool = true
|
||||
|
||||
public var arguments: TextNodeWithEntities.Arguments?
|
||||
|
||||
private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:]
|
||||
|
|
@ -293,7 +299,7 @@ public class ImmediateTextNodeWithEntities: TextNode {
|
|||
if !self.inlineStickerItemLayers.isEmpty && oldValue != self.visibility {
|
||||
for (_, itemLayer) in self.inlineStickerItemLayers {
|
||||
let isItemVisible: Bool = self.visibility
|
||||
itemLayer.isVisibleForAnimations = isItemVisible
|
||||
itemLayer.isVisibleForAnimations = self.enableLooping && isItemVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,6 +391,8 @@ public class ImmediateTextNodeWithEntities: TextNode {
|
|||
}
|
||||
|
||||
private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor) {
|
||||
self.enableLooping = context.sharedContext.energyUsageSettings.loopEmoji
|
||||
|
||||
var nextIndexById: [Int64: Int] = [:]
|
||||
var validIds: [InlineStickerItemLayer.Key] = []
|
||||
|
||||
|
|
@ -415,7 +423,7 @@ public class ImmediateTextNodeWithEntities: TextNode {
|
|||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = self.visibility
|
||||
itemLayer.isVisibleForAnimations = self.enableLooping && self.visibility
|
||||
}
|
||||
|
||||
itemLayer.frame = itemFrame
|
||||
|
|
|
|||
|
|
@ -1646,6 +1646,9 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||
extendNow = true
|
||||
}
|
||||
}
|
||||
if !sharedApplicationContext.sharedContext.energyUsageSettings.extendBackgroundWork {
|
||||
extendNow = false
|
||||
}
|
||||
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: extendNow)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
loadingPlaceholderNode.setup(self.historyNode, updating: true)
|
||||
}
|
||||
} else {
|
||||
loadingPlaceholderNode = ChatLoadingPlaceholderNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners, backgroundNode: self.backgroundNode)
|
||||
loadingPlaceholderNode = ChatLoadingPlaceholderNode(context: self.context, theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners, backgroundNode: self.backgroundNode)
|
||||
loadingPlaceholderNode.updatePresentationInterfaceState(self.chatPresentationInterfaceState)
|
||||
self.backgroundNode.supernode?.insertSubnode(loadingPlaceholderNode, aboveSubnode: self.backgroundNode)
|
||||
|
||||
|
|
@ -491,7 +491,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
||||
return
|
||||
}
|
||||
if DeviceMetrics.performance.isGraphicallyCapable {
|
||||
if strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
strongSelf.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
|
||||
}
|
||||
}
|
||||
|
|
@ -2216,7 +2216,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
||||
return
|
||||
}
|
||||
if DeviceMetrics.performance.isGraphicallyCapable {
|
||||
if self.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
self.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import ActivityIndicator
|
|||
import WallpaperBackgroundNode
|
||||
import ShimmerEffect
|
||||
import ChatPresentationInterfaceState
|
||||
import AccountContext
|
||||
|
||||
final class ChatLoadingNode: ASDisplayNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
|
|
@ -134,6 +135,8 @@ final class ChatLoadingPlaceholderMessageContainer {
|
|||
final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
private let context: AccountContext
|
||||
|
||||
private let maskNode: ASDisplayNode
|
||||
private let borderMaskNode: ASDisplayNode
|
||||
|
||||
|
|
@ -151,7 +154,8 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||
|
||||
private var validLayout: (CGSize, UIEdgeInsets)?
|
||||
|
||||
init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode) {
|
||||
init(context: AccountContext, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode) {
|
||||
self.context = context
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
self.maskNode = ASDisplayNode()
|
||||
|
|
@ -185,13 +189,13 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.backgroundColorNode)
|
||||
|
||||
if DeviceMetrics.performance.isGraphicallyCapable {
|
||||
if context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
self.containerNode.addSubnode(self.effectNode)
|
||||
}
|
||||
|
||||
self.addSubnode(self.borderNode)
|
||||
|
||||
if DeviceMetrics.performance.isGraphicallyCapable {
|
||||
if context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
self.borderNode.addSubnode(self.borderEffectNode)
|
||||
}
|
||||
}
|
||||
|
|
@ -202,7 +206,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||
self.containerNode.view.mask = self.maskNode.view
|
||||
self.borderNode.view.mask = self.borderMaskNode.view
|
||||
|
||||
if DeviceMetrics.performance.isGraphicallyCapable {
|
||||
if self.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
self.backgroundNode?.updateIsLooping(true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,7 +522,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
|||
}
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0))
|
||||
|
||||
if peer.isPremium {
|
||||
if peer.isPremium && context.sharedContext.energyUsageSettings.playVideoAvatars {
|
||||
self.cachedDataDisposable.set((context.account.postbox.peerView(id: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView in
|
||||
guard let strongSelf = self else {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return self._automaticMediaDownloadSettings.get()
|
||||
}
|
||||
|
||||
public var energyUsageSettings: EnergyUsageSettings {
|
||||
return self.currentAutomaticMediaDownloadSettings.with({ $0 }).energyUsageSettings
|
||||
}
|
||||
|
||||
public let currentAutodownloadSettings: Atomic<AutodownloadSettings>
|
||||
private let _autodownloadSettings = Promise<AutodownloadSettings>()
|
||||
private var currentAutodownloadSettingsDisposable = MetaDisposable()
|
||||
|
|
|
|||
|
|
@ -258,6 +258,73 @@ public struct MediaAutoSaveSettings: Codable, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public struct EnergyUsageSettings: Codable, Equatable {
|
||||
private enum CodingKeys: CodingKey {
|
||||
case loopEmoji
|
||||
case playVideoAvatars
|
||||
case fullTranslucency
|
||||
case extendBackgroundWork
|
||||
case synchronizeInBackground
|
||||
case autodownloadInBackground
|
||||
}
|
||||
|
||||
public static var `default`: EnergyUsageSettings {
|
||||
return EnergyUsageSettings(
|
||||
loopEmoji: true,
|
||||
playVideoAvatars: true,
|
||||
fullTranslucency: true,
|
||||
extendBackgroundWork: true,
|
||||
synchronizeInBackground: true,
|
||||
autodownloadInBackground: true
|
||||
)
|
||||
}
|
||||
|
||||
public var loopEmoji: Bool
|
||||
public var playVideoAvatars: Bool
|
||||
public var fullTranslucency: Bool
|
||||
public var extendBackgroundWork: Bool
|
||||
public var synchronizeInBackground: Bool
|
||||
public var autodownloadInBackground: Bool
|
||||
|
||||
public init(
|
||||
loopEmoji: Bool,
|
||||
playVideoAvatars: Bool,
|
||||
fullTranslucency: Bool,
|
||||
extendBackgroundWork: Bool,
|
||||
synchronizeInBackground: Bool,
|
||||
autodownloadInBackground: Bool
|
||||
) {
|
||||
self.loopEmoji = loopEmoji
|
||||
self.playVideoAvatars = playVideoAvatars
|
||||
self.fullTranslucency = fullTranslucency
|
||||
self.extendBackgroundWork = extendBackgroundWork
|
||||
self.synchronizeInBackground = synchronizeInBackground
|
||||
self.autodownloadInBackground = autodownloadInBackground
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.loopEmoji = try container.decodeIfPresent(Bool.self, forKey: .loopEmoji) ?? EnergyUsageSettings.default.loopEmoji
|
||||
self.playVideoAvatars = try container.decodeIfPresent(Bool.self, forKey: .playVideoAvatars) ?? EnergyUsageSettings.default.playVideoAvatars
|
||||
self.fullTranslucency = try container.decodeIfPresent(Bool.self, forKey: .fullTranslucency) ?? EnergyUsageSettings.default.fullTranslucency
|
||||
self.extendBackgroundWork = try container.decodeIfPresent(Bool.self, forKey: .extendBackgroundWork) ?? EnergyUsageSettings.default.extendBackgroundWork
|
||||
self.synchronizeInBackground = try container.decodeIfPresent(Bool.self, forKey: .synchronizeInBackground) ?? EnergyUsageSettings.default.synchronizeInBackground
|
||||
self.autodownloadInBackground = try container.decodeIfPresent(Bool.self, forKey: .autodownloadInBackground) ?? EnergyUsageSettings.default.autodownloadInBackground
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.loopEmoji, forKey: .loopEmoji)
|
||||
try container.encode(self.playVideoAvatars, forKey: .playVideoAvatars)
|
||||
try container.encode(self.fullTranslucency, forKey: .fullTranslucency)
|
||||
try container.encode(self.extendBackgroundWork, forKey: .extendBackgroundWork)
|
||||
try container.encode(self.synchronizeInBackground, forKey: .synchronizeInBackground)
|
||||
try container.encode(self.autodownloadInBackground, forKey: .autodownloadInBackground)
|
||||
}
|
||||
}
|
||||
|
||||
public struct MediaAutoDownloadSettings: Codable, Equatable {
|
||||
public var presets: MediaAutoDownloadPresets
|
||||
public var cellular: MediaAutoDownloadConnection
|
||||
|
|
@ -267,27 +334,30 @@ public struct MediaAutoDownloadSettings: Codable, Equatable {
|
|||
public var autoplayVideos: Bool
|
||||
public var downloadInBackground: Bool
|
||||
|
||||
public var energyUsageSettings: EnergyUsageSettings
|
||||
|
||||
public static var defaultSettings: MediaAutoDownloadSettings {
|
||||
let mb: Int64 = 1024 * 1024
|
||||
let presets = MediaAutoDownloadPresets(low: MediaAutoDownloadCategories(basePreset: .low, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||
video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
|
||||
file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)),
|
||||
medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int64(2.5 * CGFloat(mb)), predownload: false),
|
||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)),
|
||||
high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true),
|
||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false)))
|
||||
return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), autoplayGifs: true, autoplayVideos: true, downloadInBackground: true)
|
||||
video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
|
||||
file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)),
|
||||
medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int64(2.5 * CGFloat(mb)), predownload: false),
|
||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)),
|
||||
high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true),
|
||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false)))
|
||||
return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), autoplayGifs: true, autoplayVideos: true, downloadInBackground: true, energyUsageSettings: EnergyUsageSettings.default)
|
||||
}
|
||||
|
||||
public init(presets: MediaAutoDownloadPresets, cellular: MediaAutoDownloadConnection, wifi: MediaAutoDownloadConnection, autoplayGifs: Bool, autoplayVideos: Bool, downloadInBackground: Bool) {
|
||||
public init(presets: MediaAutoDownloadPresets, cellular: MediaAutoDownloadConnection, wifi: MediaAutoDownloadConnection, autoplayGifs: Bool, autoplayVideos: Bool, downloadInBackground: Bool, energyUsageSettings: EnergyUsageSettings) {
|
||||
self.presets = presets
|
||||
self.cellular = cellular
|
||||
self.wifi = wifi
|
||||
self.autoplayGifs = autoplayGifs
|
||||
self.autoplayVideos = autoplayGifs
|
||||
self.downloadInBackground = downloadInBackground
|
||||
self.energyUsageSettings = energyUsageSettings
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
|
@ -303,6 +373,8 @@ public struct MediaAutoDownloadSettings: Codable, Equatable {
|
|||
self.autoplayGifs = try container.decode(Int32.self, forKey: "autoplayGifs") != 0
|
||||
self.autoplayVideos = try container.decode(Int32.self, forKey: "autoplayVideos") != 0
|
||||
self.downloadInBackground = try container.decode(Int32.self, forKey: "downloadInBackground") != 0
|
||||
|
||||
self.energyUsageSettings = (try container.decodeIfPresent(EnergyUsageSettings.self, forKey: "energyUsageSettings")) ?? EnergyUsageSettings.default
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
|
@ -313,6 +385,7 @@ public struct MediaAutoDownloadSettings: Codable, Equatable {
|
|||
try container.encode((self.autoplayGifs ? 1 : 0) as Int32, forKey: "autoplayGifs")
|
||||
try container.encode((self.autoplayVideos ? 1 : 0) as Int32, forKey: "autoplayVideos")
|
||||
try container.encode((self.downloadInBackground ? 1 : 0) as Int32, forKey: "downloadInBackground")
|
||||
try container.encode(self.energyUsageSettings, forKey: "energyUsageSettings")
|
||||
}
|
||||
|
||||
public func connectionSettings(for networkType: MediaAutoDownloadNetworkType) -> MediaAutoDownloadConnection {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue