Low power experiments

This commit is contained in:
Ali 2023-02-14 17:48:09 +04:00
parent e7fdd14b23
commit bb7ffa6191
24 changed files with 543 additions and 115 deletions

View file

@ -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>

View file

@ -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 }

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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() {

View file

@ -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)

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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)
})

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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()

View file

@ -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 {