Wallpaper experiment

This commit is contained in:
Ali 2021-11-05 20:35:30 +04:00
parent 0679e4dd7e
commit cdc04c733c
23 changed files with 835 additions and 99 deletions

View File

@ -80,7 +80,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case experimentalCompatibility(Bool) case experimentalCompatibility(Bool)
case enableDebugDataDisplay(Bool) case enableDebugDataDisplay(Bool)
case acceleratedStickers(Bool) case acceleratedStickers(Bool)
case mockICE(Bool) case experimentalBackground(Bool)
case playerEmbedding(Bool) case playerEmbedding(Bool)
case playlistPlayback(Bool) case playlistPlayback(Bool)
case voiceConference case voiceConference
@ -102,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .mockICE: case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec: case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue return DebugControllerSection.videoExperiments.rawValue
@ -171,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 27 return 27
case .acceleratedStickers: case .acceleratedStickers:
return 29 return 29
case .mockICE: case .experimentalBackground:
return 30 return 30
case .playerEmbedding: case .playerEmbedding:
return 31 return 31
@ -804,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
}).start() }).start()
}) })
case let .mockICE(value): case let .experimentalBackground(value):
return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
settings.mockICE = value settings.experimentalBackground = value
return PreferencesEntry(settings) return PreferencesEntry(settings)
}) })
}).start() }).start()
@ -926,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility)) entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers)) entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
entries.append(.mockICE(experimentalSettings.mockICE)) entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
} }

View File

@ -518,7 +518,22 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
} }
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) { public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) {
let node = self.node
let _ = (self.node.status
|> take(1)
|> deliverOnMainQueue).start(next: { [weak node] status in
if let node = node, let timestamp = status?.timestamp, let duration = status?.duration {
let nextTimestamp = timestamp + skipInterval.seconds
if nextTimestamp > duration {
node.seek(0.0)
node.pause()
} else {
node.seek(min(duration, nextTimestamp))
}
}
completionHandler() completionHandler()
})
} }
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool { public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
private var hiddenMediaManagerIndex: Int? private var hiddenMediaManagerIndex: Int?
init(overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) { private var messageRemovedDisposable: Disposable?
init(context: AccountContext, overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, canSkip: Bool, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) {
self.overlayController = overlayController self.overlayController = overlayController
self.mediaManager = mediaManager self.mediaManager = mediaManager
self.node = videoNode self.node = videoNode
@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
contentDelegate.pictureInPictureController = pictureInPictureController contentDelegate.pictureInPictureController = pictureInPictureController
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
pictureInPictureController.requiresLinearPlayback = true pictureInPictureController.requiresLinearPlayback = !canSkip
pictureInPictureController.delegate = self pictureInPictureController.delegate = self
self.pictureInPictureController = pictureInPictureController self.pictureInPictureController = pictureInPictureController
let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in
@ -586,9 +603,31 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
} }
}) })
} }
if let (messageId, _) = hiddenMedia {
self.messageRemovedDisposable = (context.account.postbox.combinedView(keys: [PostboxViewKey.messages([messageId])])
|> map { views -> Bool in
if let view = views.views[PostboxViewKey.messages([messageId])] as? MessagesView {
if view.messages[messageId] == nil {
return true
}
}
return false
}
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
overlayController.removePictureInPictureContent(content: strongSelf)
strongSelf.node.canAttachContent = false
})
}
} }
deinit { deinit {
self.messageRemovedDisposable?.dispose()
self.pictureInPictureTimer?.invalidate() self.pictureInPictureTimer?.invalidate()
self.node.setCanPlaybackWithoutHierarchy(false) self.node.setCanPlaybackWithoutHierarchy(false)
@ -699,6 +738,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
private var keepSoundOnDismiss = false private var keepSoundOnDismiss = false
private var hasPictureInPicture = false private var hasPictureInPicture = false
private var pictureInPictureButton: UIBarButtonItem?
private var requiresDownload = false private var requiresDownload = false
private var item: UniversalVideoGalleryItem? private var item: UniversalVideoGalleryItem?
@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
} }
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) { if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
self.pictureInPictureButton = rightBarButtonItem
barButtonItems.append(rightBarButtonItem) barButtonItems.append(rightBarButtonItem)
self.hasPictureInPicture = true self.hasPictureInPicture = true
} else { } else {
@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
pictureInPictureNode.removeFromSupernode() pictureInPictureNode.removeFromSupernode()
self.videoNode?.backgroundColor = .black self.videoNode?.backgroundColor = .black
} }
self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil
} }
private func shouldAutoplayOnCentrality() -> Bool { private func shouldAutoplayOnCentrality() -> Bool {
@ -2069,9 +2113,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
case let .message(message): case let .message(message):
for media in message.media { for media in message.media {
if let media = media as? TelegramMediaFile, media.isVideo { if let media = media as? TelegramMediaFile, media.isVideo {
if message.id.namespace == Namespaces.Message.Cloud {
isNativePictureInPictureSupported = true isNativePictureInPictureSupported = true
} }
} }
}
default: default:
break break
} }
@ -2106,7 +2152,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
break break
} }
let content = PictureInPictureContentImpl(overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, willBegin: { [weak self] content in let content = PictureInPictureContentImpl(context: self.context, overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, canSkip: true, willBegin: { [weak self] content in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
deinit { deinit {
} }
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
let sizeUpdated = self.validLayout != size let sizeUpdated = self.validLayout != size
self.validLayout = size self.validLayout = size
@ -372,6 +372,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25 animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
} }
animation.completion = { _ in
completion()
}
self.contentView.layer.removeAnimation(forKey: "contents") self.contentView.layer.removeAnimation(forKey: "contents")
self.contentView.layer.add(animation, forKey: "contents") self.contentView.layer.add(animation, forKey: "contents")
@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
for cloneNode in self.cloneNodes { for cloneNode in self.cloneNodes {
cloneNode.value?.image = dimmedImage cloneNode.value?.image = dimmedImage
} }
completion()
} }
} else {
completion()
} }
} else if sizeUpdated { } else if sizeUpdated {
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions) let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
} }
self.validPhase = self.phase self.validPhase = self.phase
completion()
} else {
completion()
} }
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
self.colors = colors self.colors = colors
self.invalidated = true self.invalidated = true
if let size = self.validLayout { if let size = self.validLayout {
self.updateLayout(size: size, transition: .immediate) self.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
} }
} }
} }
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
guard case let .animated(duration, _) = transition, duration > 0.001 else { guard case let .animated(duration, _) = transition, duration > 0.001 else {
completion()
return return
} }
@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
GradientBackgroundNode.sharedPhase = self.phase GradientBackgroundNode.sharedPhase = self.phase
} }
if let size = self.validLayout { if let size = self.validLayout {
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards) self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion)
} else {
completion()
} }
} }
} }

View File

@ -94,7 +94,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
strongSelf.inputFieldNode.append(character) strongSelf.inputFieldNode.append(character)
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring)) gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {})
} }
} }
} }
@ -102,7 +102,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
let _ = strongSelf.inputFieldNode.delete() let _ = strongSelf.inputFieldNode.delete()
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true) gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
} }
} }
} }
@ -167,7 +167,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.hapticFeedback.tap() self.hapticFeedback.tap()
let result = self.inputFieldNode.delete() let result = self.inputFieldNode.delete()
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true) gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
} }
} }
@ -337,7 +337,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true) gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {})
} }
} }
self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none) self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none)
@ -355,7 +355,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring)) gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {})
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
if !iconFrame.isEmpty { if !iconFrame.isEmpty {
@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring)) gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {})
} }
self.inputFieldNode.animateIn() self.inputFieldNode.animateIn()
self.keyboardNode.animateIn() self.keyboardNode.animateIn()
@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.hapticFeedback.error() self.hapticFeedback.error()
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true) gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {})
} }
} }
@ -440,7 +440,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let backgroundCustomNode = self.backgroundCustomNode { if let backgroundCustomNode = self.backgroundCustomNode {
transition.updateFrame(node: backgroundCustomNode, frame: bounds) transition.updateFrame(node: backgroundCustomNode, frame: bounds)
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode { if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition) gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition, extendAnimation: false, backwards: false, completion: {})
} }
} }
transition.updateFrame(view: self.effectView, frame: bounds) transition.updateFrame(view: self.effectView, frame: bounds)

View File

@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
self.scrollNode = ASScrollNode() self.scrollNode = ASScrollNode()
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.chatBackgroundNode.displaysAsynchronously = false self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode()

View File

@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
return { item, params, neighbors in return { item, params, neighbors in
if currentBackgroundNode == nil { if currentBackgroundNode == nil {
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
} }
currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.update(wallpaper: item.wallpaper)
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)

View File

@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4)) self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
self.chatListBackgroundNode = ASDisplayNode() self.chatListBackgroundNode = ASDisplayNode()
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context) self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.chatBackgroundNode.displaysAsynchronously = false self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode()

View File

@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
if let gradientNode = self.gradientNode { if let gradientNode = self.gradientNode {
gradientNode.frame = CGRect(origin: CGPoint(), size: size) gradientNode.frame = CGRect(origin: CGPoint(), size: size)
gradientNode.updateLayout(size: size, transition: .immediate) gradientNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
} }
let progressDiameter: CGFloat = 50.0 let progressDiameter: CGFloat = 50.0

View File

@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.backgroundContainerNode = ASDisplayNode() self.backgroundContainerNode = ASDisplayNode()
self.backgroundContainerNode.clipsToBounds = true self.backgroundContainerNode.clipsToBounds = true
self.backgroundWrapperNode = ASDisplayNode() self.backgroundWrapperNode = ASDisplayNode()
self.backgroundNode = createWallpaperBackgroundNode(context: context) self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.clipsToBounds = true

View File

@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.clipsToBounds = true
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.instantChatBackgroundNode.displaysAsynchronously = false self.instantChatBackgroundNode.displaysAsynchronously = false
self.ready.set(.single(true)) self.ready.set(.single(true))
@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.blurredNode = BlurredImageNode() self.blurredNode = BlurredImageNode()
self.blurredNode.blurView.contentMode = .scaleAspectFill self.blurredNode.blurView.contentMode = .scaleAspectFill
self.wallpaperNode = createWallpaperBackgroundNode(context: context) self.wallpaperNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set) self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set)

View File

@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
return { item, params, neighbors in return { item, params, neighbors in
if currentBackgroundNode == nil { if currentBackgroundNode == nil {
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context) currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
} }
currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.update(wallpaper: item.wallpaper)
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)

View File

@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.wrapperNode = ASDisplayNode() self.wrapperNode = ASDisplayNode()
self.imageNode = TransformImageNode() self.imageNode = TransformImageNode()
self.imageNode.contentAnimations = .subsequentUpdates self.imageNode.contentAnimations = .subsequentUpdates
self.nativeNode = createWallpaperBackgroundNode(context: context) self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.cropNode = WallpaperCropNode() self.cropNode = WallpaperCropNode()
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6)) self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter) self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)

View File

@ -964,7 +964,7 @@ final class MessageStoryRenderer {
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context) self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
self.instantChatBackgroundNode.displaysAsynchronously = false self.instantChatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode()

View File

@ -7,6 +7,6 @@
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data); NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size); UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor); UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque);
#endif /* Lottie_h */ #endif /* Lottie_h */

View File

@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) {
@end @end
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor) { UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor, bool opaque) {
NSDate *startTime = [NSDate date]; NSDate *startTime = [NSDate date];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
@ -116,7 +116,7 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
startTime = [NSDate date]; startTime = [NSDate date];
UIGraphicsBeginImageContextWithOptions(size, true, 1.0); UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, backgroundColor.CGColor); CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));

View File

@ -2001,7 +2001,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
} else { } else {
pollPts = 1 pollPts = 1
} }
return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: pollPts, limit: limit)) return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: max(pollPts, 1), limit: limit))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in |> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
switch error.errorDescription { switch error.errorDescription {

View File

@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
default: default:
break break
} }
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase) self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground)
self.wallpaperReady.set(self.chatBackgroundNode.isReady) self.wallpaperReady.set(self.chatBackgroundNode.isReady)
var locationBroadcastPanelSource: LocationBroadcastPanelSource var locationBroadcastPanelSource: LocationBroadcastPanelSource

View File

@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return return
} }
var adPeerId: PeerId? var adPeerId: PeerId?
adPeerId = nil adPeerId = messages.first?.author?.id
if strongSelf.preloadAdPeerId != adPeerId { if strongSelf.preloadAdPeerId != adPeerId {
strongSelf.preloadAdPeerId = adPeerId strongSelf.preloadAdPeerId = adPeerId

View File

@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 } self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
self.backgroundNode = createWallpaperBackgroundNode(context: context) self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true)
self.backgroundNode.isUserInteractionEnabled = false self.backgroundNode.isUserInteractionEnabled = false
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor) self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)

View File

@ -18,7 +18,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
public var experimentalCompatibility: Bool public var experimentalCompatibility: Bool
public var enableDebugDataDisplay: Bool public var enableDebugDataDisplay: Bool
public var acceleratedStickers: Bool public var acceleratedStickers: Bool
public var mockICE: Bool public var experimentalBackground: Bool
public static var defaultSettings: ExperimentalUISettings { public static var defaultSettings: ExperimentalUISettings {
return ExperimentalUISettings( return ExperimentalUISettings(
@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: false, experimentalCompatibility: false,
enableDebugDataDisplay: false, enableDebugDataDisplay: false,
acceleratedStickers: false, acceleratedStickers: false,
mockICE: false experimentalBackground: false
) )
} }
@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: Bool, experimentalCompatibility: Bool,
enableDebugDataDisplay: Bool, enableDebugDataDisplay: Bool,
acceleratedStickers: Bool, acceleratedStickers: Bool,
mockICE: Bool experimentalBackground: Bool
) { ) {
self.keepChatNavigationStack = keepChatNavigationStack self.keepChatNavigationStack = keepChatNavigationStack
self.skipReadHistory = skipReadHistory self.skipReadHistory = skipReadHistory
@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = experimentalCompatibility self.experimentalCompatibility = experimentalCompatibility
self.enableDebugDataDisplay = enableDebugDataDisplay self.enableDebugDataDisplay = enableDebugDataDisplay
self.acceleratedStickers = acceleratedStickers self.acceleratedStickers = acceleratedStickers
self.mockICE = mockICE self.experimentalBackground = experimentalBackground
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -91,7 +91,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0 self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0 self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0 self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
self.mockICE = (try container.decodeIfPresent(Int32.self, forKey: "mockICE") ?? 0) != 0 self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -111,7 +111,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility") try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay") try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers") try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
try container.encode((self.mockICE ? 1 : 0) as Int32, forKey: "mockICE") try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground")
} }
} }

View File

@ -19,6 +19,9 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/WallpaperResources:WallpaperResources", "//submodules/WallpaperResources:WallpaperResources",
"//submodules/FastBlur:FastBlur", "//submodules/FastBlur:FastBlur",
"//submodules/Svg:Svg",
"//submodules/GZip:GZip",
"//submodules/AppBundle:AppBundle",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -9,6 +9,9 @@ import AccountContext
import SwiftSignalKit import SwiftSignalKit
import WallpaperResources import WallpaperResources
import FastBlur import FastBlur
import Svg
import GZip
import AppBundle
private let motionAmount: CGFloat = 32.0 private let motionAmount: CGFloat = 32.0
@ -781,12 +784,12 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
if let gradientBackgroundNode = self.gradientBackgroundNode { if let gradientBackgroundNode = self.gradientBackgroundNode {
transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
gradientBackgroundNode.updateLayout(size: size, transition: transition) gradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
} }
if let outgoingBubbleGradientBackgroundNode = self.outgoingBubbleGradientBackgroundNode { if let outgoingBubbleGradientBackgroundNode = self.outgoingBubbleGradientBackgroundNode {
transition.updateFrame(node: outgoingBubbleGradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: outgoingBubbleGradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition) outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
} }
self.loadPatternForSizeIfNeeded(size: size, transition: transition) self.loadPatternForSizeIfNeeded(size: size, transition: transition)
@ -797,21 +800,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
} }
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation) self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
}
private func updateBakedBackground() {
guard let size = self.validLayout else {
return
}
let context = DrawingContext(size: size, scale: UIScreenScale, opaque: true)
context.withContext { context in
context.clear(CGRect(origin: CGPoint(), size: size))
}
self.bakedBackgroundView.image = context.generateImage()
} }
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
@ -824,7 +814,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false) let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
if let size = self.validLayout { if let size = self.validLayout {
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate) outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
} }
self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode
} }
@ -898,32 +888,473 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
} }
} }
private protocol WallpaperComponentView: AnyObject {
var view: UIView { get }
func update(size: CGSize, transition: ContainedViewLayoutTransition)
}
final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode { final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode {
final class SharedStorage { final class SharedStorage {
} }
private class WallpaperComponentView: UIView { final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode {
let updated: () -> Void private let bubbleType: WallpaperBubbleType
private let contentNode: ASImageNode
init(updated: @escaping () -> Void) { private var cleanWallpaperNode: ASDisplayNode?
self.updated = updated private var gradientWallpaperNode: GradientBackgroundNode.CloneNode?
private weak var backgroundNode: WallpaperBackgroundNodeMergedImpl?
private var index: SparseBag<BubbleBackgroundNodeImpl>.Index?
super.init(frame: CGRect()) private var currentLayout: (rect: CGRect, containerSize: CGSize)?
override var frame: CGRect {
didSet {
if oldValue.size != self.bounds.size {
self.contentNode.frame = self.bounds
if let cleanWallpaperNode = self.cleanWallpaperNode {
cleanWallpaperNode.frame = self.bounds
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
gradientWallpaperNode.frame = self.bounds
}
}
}
}
init(backgroundNode: WallpaperBackgroundNodeMergedImpl, bubbleType: WallpaperBubbleType) {
self.backgroundNode = backgroundNode
self.bubbleType = bubbleType
self.contentNode = ASImageNode()
self.contentNode.displaysAsynchronously = false
self.contentNode.isUserInteractionEnabled = false
super.init()
self.addSubnode(self.contentNode)
self.index = backgroundNode.bubbleBackgroundNodeReferences.add(BubbleBackgroundNodeReference(node: self))
}
deinit {
if let index = self.index, let backgroundNode = self.backgroundNode {
backgroundNode.bubbleBackgroundNodeReferences.remove(index)
}
}
func updateContents() {
guard let backgroundNode = self.backgroundNode else {
return
}
if let bubbleTheme = backgroundNode.bubbleTheme, let bubbleCorners = backgroundNode.bubbleCorners {
let wallpaper = backgroundNode.wallpaper ?? bubbleTheme.chat.defaultWallpaper
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
var needsCleanBackground = false
switch self.bubbleType {
case .incoming:
self.contentNode.image = graphics.incomingBubbleGradientImage
if graphics.incomingBubbleGradientImage == nil {
self.contentNode.backgroundColor = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill[0]
} else {
self.contentNode.backgroundColor = nil
}
needsCleanBackground = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 })
case .outgoing:
if backgroundNode.outgoingBubbleGradientBackgroundNode != nil {
self.contentNode.image = nil
self.contentNode.backgroundColor = nil
} else {
self.contentNode.image = graphics.outgoingBubbleGradientImage
if graphics.outgoingBubbleGradientImage == nil {
self.contentNode.backgroundColor = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill[0]
} else {
self.contentNode.backgroundColor = nil
}
needsCleanBackground = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 })
}
case .free:
self.contentNode.image = nil
self.contentNode.backgroundColor = nil
needsCleanBackground = true
}
var isInvertedGradient = false
var hasComplexGradient = false
switch wallpaper {
case let .file(file):
hasComplexGradient = file.settings.colors.count >= 3
if let intensity = file.settings.intensity, intensity < 0 {
isInvertedGradient = true
}
case let .gradient(gradient):
hasComplexGradient = gradient.colors.count >= 3
default:
break
}
var needsGradientBackground = false
var needsWallpaperBackground = false
if isInvertedGradient {
switch self.bubbleType {
case .free:
needsCleanBackground = false
case .incoming, .outgoing:
break
}
}
if needsCleanBackground {
if hasComplexGradient {
needsGradientBackground = backgroundNode.gradient != nil
} else {
needsWallpaperBackground = true
}
}
var gradientBackgroundSource: GradientBackgroundNode? = backgroundNode.gradient?.gradientBackground
if case .outgoing = self.bubbleType {
if let outgoingBubbleGradientBackgroundNode = backgroundNode.outgoingBubbleGradientBackgroundNode {
gradientBackgroundSource = outgoingBubbleGradientBackgroundNode
needsWallpaperBackground = false
needsGradientBackground = true
}
}
if needsWallpaperBackground {
if self.cleanWallpaperNode == nil {
let cleanWallpaperNode = ASImageNode()
cleanWallpaperNode.displaysAsynchronously = false
self.cleanWallpaperNode = cleanWallpaperNode
cleanWallpaperNode.frame = self.bounds
self.insertSubnode(cleanWallpaperNode, at: 0)
}
if let blurredBackgroundContents = backgroundNode.blurredBackgroundContents {
self.cleanWallpaperNode?.contents = blurredBackgroundContents.cgImage
self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor
} else {
self.cleanWallpaperNode?.contents = nil
self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor
}
} else {
if let cleanWallpaperNode = self.cleanWallpaperNode {
self.cleanWallpaperNode = nil
cleanWallpaperNode.removeFromSupernode()
}
}
if needsGradientBackground, let gradientBackgroundNode = gradientBackgroundSource {
if self.gradientWallpaperNode == nil {
let gradientWallpaperNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode)
gradientWallpaperNode.frame = self.bounds
self.gradientWallpaperNode = gradientWallpaperNode
self.insertSubnode(gradientWallpaperNode, at: 0)
}
} else {
if let gradientWallpaperNode = self.gradientWallpaperNode {
self.gradientWallpaperNode = nil
gradientWallpaperNode.removeFromSupernode()
}
}
} else {
self.contentNode.image = nil
if let cleanWallpaperNode = self.cleanWallpaperNode {
self.cleanWallpaperNode = nil
cleanWallpaperNode.removeFromSupernode()
}
}
if let (rect, containerSize) = self.currentLayout {
self.update(rect: rect, within: containerSize)
}
}
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
self.currentLayout = (rect, containerSize)
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds)
transition.animateView {
self.contentNode.layer.contentsRect = shiftedContentsRect
}
if let cleanWallpaperNode = self.cleanWallpaperNode {
transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds)
transition.animateView {
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect
}
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds)
transition.animateView {
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect
}
}
}
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.currentLayout = (rect, containerSize)
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds)
self.contentNode.layer.contentsRect = shiftedContentsRect
if let cleanWallpaperNode = self.cleanWallpaperNode {
transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds)
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds)
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect
}
}
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
guard let (_, containerSize) = self.currentLayout else {
return
}
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
let scaledOffset = CGPoint(x: value.x / containerSize.width, y: value.y / containerSize.height)
transition.animateContentsRectPositionAdditive(layer: self.contentNode.layer, offset: scaledOffset)
if let cleanWallpaperNode = self.cleanWallpaperNode {
transition.animateContentsRectPositionAdditive(layer: cleanWallpaperNode.layer, offset: scaledOffset)
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
transition.animateContentsRectPositionAdditive(layer: gradientWallpaperNode.layer, offset: scaledOffset)
}
}
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
guard let (_, containerSize) = self.currentLayout else {
return
}
let scaledOffset = CGPoint(x: 0.0, y: -value / containerSize.height)
self.contentNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
if let cleanWallpaperNode = self.cleanWallpaperNode {
cleanWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
gradientWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
}
}
}
private final class BubbleBackgroundNodeReference {
weak var node: BubbleBackgroundNodeImpl?
init(node: BubbleBackgroundNodeImpl) {
self.node = node
}
}
private final class WallpaperGradiendComponentView: WallpaperComponentView {
struct Spec: Equatable {
var colors: [UInt32]
}
let spec: Spec
let gradientBackground: GradientBackgroundNode
var view: UIView {
return self.gradientBackground.view
}
init(spec: Spec, updated: @escaping () -> Void) {
self.spec = spec
self.gradientBackground = GradientBackgroundNode(colors: spec.colors.map(UIColor.init(rgb:)), useSharedAnimationPhase: true, adjustSaturation: false)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
}
private final class WallpaperGradiendComponentView: WallpaperComponentView { func update(size: CGSize, transition: ContainedViewLayoutTransition) {
struct Spec { self.gradientBackground.frame = CGRect(origin: CGPoint(), size: size)
var colors: [UInt32] self.gradientBackground.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
} }
} }
private final class WallpaperPatternComponentView: WallpaperComponentView { private final class WallpaperColorComponentView: WallpaperComponentView {
struct Spec { struct Spec: Equatable {
var color: UInt32
}
let spec: Spec
let backgroundView: UIView
var view: UIView {
return self.backgroundView
}
init(spec: Spec, updated: @escaping () -> Void) {
self.spec = spec
self.backgroundView = UIView()
self.backgroundView.backgroundColor = UIColor(rgb: spec.color)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
}
}
private final class WallpaperImageComponentView: WallpaperComponentView {
enum Spec: Equatable {
case image(
representation: TelegramMediaImageRepresentation,
isPattern: Bool,
intensity: CGFloat
)
case builtin
}
let spec: Spec
let updated: () -> Void
let imageView: UIImageView
var fetchDisposable: Disposable?
var dataDisposable: Disposable?
var imageData: Data?
private var validSize: CGSize?
var view: UIView {
return self.imageView
}
init(context: AccountContext, spec: Spec, updated: @escaping () -> Void) {
self.spec = spec
self.updated = updated
self.imageView = UIImageView()
self.imageView.contentMode = .scaleAspectFill
switch spec {
case let .image(representation, _, _):
self.fetchDisposable = (fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: MediaResourceReference.standalone(resource: representation.resource))
|> deliverOnMainQueue).start()
self.dataDisposable = (context.account.postbox.mediaBox.resourceData(representation.resource)
|> deliverOnMainQueue).start(next: { [weak self] dataValue in
guard let strongSelf = self else {
return
}
if dataValue.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: dataValue.path)) {
strongSelf.imageData = data
if let size = strongSelf.validSize {
strongSelf.updateImage(size: size, data: data)
}
}
})
case .builtin:
if let filePath = getAppBundle().path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) {
self.imageData = data
if let size = self.validSize {
self.updateImage(size: size, data: data)
}
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.fetchDisposable?.dispose()
self.dataDisposable?.dispose()
}
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
let sizeUpdated = self.validSize != size
self.validSize = size
self.imageView.frame = CGRect(origin: CGPoint(), size: size)
if sizeUpdated || self.imageView.image == nil {
if let imageData = self.imageData {
self.updateImage(size: size, data: imageData)
}
}
}
private func updateImage(size: CGSize, data: Data) {
let scale: CGFloat
if UIScreenScale >= 2.9 {
scale = 2.5
} else {
scale = UIScreenScale
}
switch self.spec {
case let .image(_, isPattern, intensity):
if isPattern {
let patternBackgroundColor: UIColor
let patternForegroundColor: UIColor
if intensity < 0.0 {
patternBackgroundColor = .clear
patternForegroundColor = .black
} else {
patternBackgroundColor = .clear
patternForegroundColor = .black
}
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let patternImage = drawSvgImage(unpackedData, CGSize(width: floor(size.width * scale), height: floor(size.height * scale)), patternBackgroundColor, patternForegroundColor, false) {
if intensity < 0.0 {
self.imageView.image = generateImage(patternImage.size, scale: patternImage.scale, rotatedContext: { size, context in
context.setFillColor(UIColor.black.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
if let cgImage = patternImage.cgImage {
context.setBlendMode(.destinationOut)
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
}
})
self.imageView.alpha = 1.0
self.imageView.layer.compositingFilter = nil
self.imageView.backgroundColor = UIColor(white: 0.0, alpha: 1.0 - abs(intensity))
} else {
self.imageView.image = patternImage
self.imageView.alpha = abs(intensity)
self.imageView.layer.compositingFilter = "softLightBlendMode"
self.imageView.backgroundColor = nil
}
}
self.updated()
} else if let image = UIImage(data: data) {
self.imageView.image = image
self.imageView.layer.compositingFilter = nil
self.imageView.alpha = 1.0
self.updated()
}
case .builtin:
if let image = UIImage(data: data) {
self.imageView.image = image
self.imageView.layer.compositingFilter = nil
self.imageView.alpha = 1.0
self.updated()
}
}
} }
} }
@ -931,8 +1362,17 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
private let storage: SharedStorage private let storage: SharedStorage
private let staticView: UIImageView private let staticView: UIImageView
private let dynamicView: UIView
private var color: WallpaperColorComponentView?
private var gradient: WallpaperGradiendComponentView? private var gradient: WallpaperGradiendComponentView?
private var pattern: WallpaperPatternComponentView? private var image: WallpaperImageComponentView?
private var blurredBackgroundContents: UIImage?
private var isSettingUpWallpaper: Bool = false
private var wallpaper: TelegramWallpaper?
private var validLayout: CGSize?
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true) private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
var isReady: Signal<Bool, NoError> { var isReady: Signal<Bool, NoError> {
@ -944,11 +1384,19 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
} }
} }
private var isAnimating: Bool = false
private var bubbleTheme: PresentationTheme?
private var bubbleCorners: PresentationChatBubbleCorners?
private var bubbleBackgroundNodeReferences = SparseBag<BubbleBackgroundNodeReference>()
private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode?
init(context: AccountContext, storage: SharedStorage?) { init(context: AccountContext, storage: SharedStorage?) {
self.context = context self.context = context
self.storage = storage ?? SharedStorage() self.storage = storage ?? SharedStorage()
self.staticView = UIImageView() self.staticView = UIImageView()
self.dynamicView = UIView()
super.init() super.init()
@ -956,70 +1404,294 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
} }
func update(wallpaper: TelegramWallpaper) { func update(wallpaper: TelegramWallpaper) {
self.wallpaper = wallpaper
var colorSpec: WallpaperColorComponentView.Spec?
var gradientSpec: WallpaperGradiendComponentView.Spec? var gradientSpec: WallpaperGradiendComponentView.Spec?
var imageSpec: WallpaperImageComponentView.Spec?
switch wallpaper { switch wallpaper {
case let .builtin(wallpaperSettings): case .builtin:
let _ = wallpaperSettings imageSpec = WallpaperImageComponentView.Spec.builtin
case let .color(color): case let .color(color):
let _ = color colorSpec = WallpaperColorComponentView.Spec(color: color)
case let .gradient(gradient): case let .gradient(gradient):
if gradient.colors.count >= 3 { if gradient.colors.count >= 3 {
gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors) gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors)
} }
case let .image(representations, settings): case let .image(representations, settings):
let _ = representations if let representation = representations.last {
imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: false, intensity: 1.0)
}
let _ = settings let _ = settings
case let .file(file): case let .file(file):
if file.settings.colors.count >= 3 { if file.settings.colors.count >= 3 {
gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors) gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors)
} }
if let dimensions = file.file.dimensions {
let representation = TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: file.file.immediateThumbnailData)
imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: file.isPattern, intensity: CGFloat(file.settings.intensity ?? 100) / 100.0)
}
} }
if let gradientSpec = gradientSpec { if self.color?.spec != colorSpec {
let gradient: WallpaperGradiendComponentView if let color = self.color {
if let current = self.gradient { self.color = nil
gradient = current color.view.removeFromSuperview()
} else {
gradient = WallpaperGradiendComponentView(updated: { [weak self] in
self?.componentsUpdated()
})
} }
let _ = gradient if let colorSpec = colorSpec {
let _ = gradientSpec let color = WallpaperColorComponentView(spec: colorSpec, updated: { [weak self] in
} else if let gradient = self.gradient { guard let strongSelf = self else {
return
}
strongSelf.componentsUpdated()
})
self.color = color
if let size = self.validLayout {
color.update(size: size, transition: .immediate)
}
self.dynamicView.insertSubview(color.view, at: 0)
self.componentsUpdated()
}
}
if self.gradient?.spec != gradientSpec {
if let gradient = self.gradient {
self.gradient = nil self.gradient = nil
gradient.removeFromSuperview() gradient.view.removeFromSuperview()
}
if let gradientSpec = gradientSpec {
let gradient = WallpaperGradiendComponentView(spec: gradientSpec, updated: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.componentsUpdated()
})
self.gradient = gradient
if let size = self.validLayout {
gradient.update(size: size, transition: .immediate)
}
self.dynamicView.insertSubview(gradient.view, at: 0)
}
}
if self.image?.spec != imageSpec {
if let image = self.image {
self.image = nil
image.view.removeFromSuperview()
}
if let imageSpec = imageSpec {
let image = WallpaperImageComponentView(context: self.context, spec: imageSpec, updated: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.componentsUpdated()
})
self.image = image
if let size = self.validLayout {
image.update(size: size, transition: .immediate)
}
if let gradient = self.gradient {
self.dynamicView.insertSubview(image.view, aboveSubview: gradient.view)
} else {
self.dynamicView.insertSubview(image.view, at: 0)
}
}
} }
} }
private func componentsUpdated() { private func componentsUpdated() {
if self.isAnimating {
if self.dynamicView.superview == nil {
self.view.addSubview(self.dynamicView)
self.staticView.isHidden = true
}
self._isReady.set(true)
} else {
self.staticView.isHidden = false
self.dynamicView.removeFromSuperview()
if let size = self.validLayout {
if let color = self.color {
self.staticView.image = nil
self.staticView.backgroundColor = color.backgroundView.backgroundColor
} else {
let gradientImage = self.gradient?.gradientBackground.contentView.image
let gradientFrame = self.gradient?.gradientBackground.frame
let imageImage = self.image?.imageView.image
let imageBackgroundColor = self.image?.imageView.backgroundColor
let imageFrame = self.image?.imageView.frame
let imageAlpha = self.image?.imageView.alpha
let imageFilter = self.image?.imageView.layer.compositingFilter as? String
self.staticView.image = generateImage(size, opaque: true, scale: nil, rotatedContext: { size, context in
UIGraphicsPushContext(context)
if let gradientImage = gradientImage, let gradientFrame = gradientFrame {
gradientImage.draw(in: gradientFrame)
}
if let imageImage = imageImage, let imageFrame = imageFrame, let imageAlpha = imageAlpha {
if imageFilter == "softLightBlendMode" {
context.setBlendMode(.softLight)
}
if let imageBackgroundColor = imageBackgroundColor {
context.setFillColor(imageBackgroundColor.cgColor)
context.fill(imageFrame)
}
context.setAlpha(imageAlpha)
context.translateBy(x: imageFrame.midX, y: imageFrame.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY)
if let cgImage = imageImage.cgImage {
let drawingSize = imageImage.size.aspectFilled(imageFrame.size)
context.draw(cgImage, in: CGRect(origin: CGPoint(x: imageFrame.minX + (imageFrame.width - drawingSize.width) / 2.0, y: imageFrame.minX + (imageFrame.height - drawingSize.height) / 2.0), size: drawingSize))
}
context.translateBy(x: imageFrame.midX, y: imageFrame.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY)
context.setBlendMode(.normal)
context.setAlpha(1.0)
}
UIGraphicsPopContext()
})
}
self._isReady.set(true)
}
}
} }
func _internalUpdateIsSettingUpWallpaper() { func _internalUpdateIsSettingUpWallpaper() {
self.isSettingUpWallpaper = true
} }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
self.staticView.frame = CGRect(origin: CGPoint(), size: size)
if let gradient = self.gradient {
gradient.update(size: size, transition: transition)
}
if let image = self.image {
image.update(size: size, transition: transition)
}
} }
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
if let gradient = self.gradient {
self.isAnimating = true
self.componentsUpdated()
gradient.gradientBackground.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.isAnimating = false
strongSelf.componentsUpdated()
})
} else {
self.isAnimating = false
}
} }
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) { func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners {
self.bubbleTheme = bubbleTheme
self.bubbleCorners = bubbleCorners
if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && bubbleTheme.chat.animateMessageColors {
if self.outgoingBubbleGradientBackgroundNode == nil {
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
if let size = self.validLayout {
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
}
self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode
}
self.outgoingBubbleGradientBackgroundNode?.updateColors(colors: bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill)
} else if let _ = self.outgoingBubbleGradientBackgroundNode {
self.outgoingBubbleGradientBackgroundNode = nil
}
self.updateBubbles()
}
}
private func updateBubbles() {
for reference in self.bubbleBackgroundNodeReferences {
reference.node?.updateContents()
}
} }
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool { func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool {
guard let bubbleTheme = self.bubbleTheme, let bubbleCorners = self.bubbleCorners else {
return false
}
if self.wallpaper == nil && !self.isSettingUpWallpaper {
return false
}
var hasPlainWallpaper = false
let graphicsWallpaper: TelegramWallpaper
if let wallpaper = self.wallpaper {
switch wallpaper {
case .color:
hasPlainWallpaper = true
default:
break
}
graphicsWallpaper = wallpaper
} else {
graphicsWallpaper = bubbleTheme.chat.defaultWallpaper
}
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: graphicsWallpaper, bubbleCorners: bubbleCorners)
switch type {
case .incoming:
if graphics.incomingBubbleGradientImage != nil {
return true
}
if bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) {
return !hasPlainWallpaper
}
case .outgoing:
if graphics.outgoingBubbleGradientImage != nil {
return true
}
if bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) {
return !hasPlainWallpaper
}
case .free:
return true
}
return false return false
} }
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? { func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
if !self.hasBubbleBackground(for: type) {
return nil return nil
} }
let node = WallpaperBackgroundNodeMergedImpl.BubbleBackgroundNodeImpl(backgroundNode: self, bubbleType: type)
node.updateContents()
return node
}
}
private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage()
public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false, useExperimentalImplementation: Bool = false) -> WallpaperBackgroundNode {
if forChatDisplay && useExperimentalImplementation {
return WallpaperBackgroundNodeMergedImpl(context: context, storage: useSharedAnimationPhase ? sharedStorage : nil)
} }
public func createWallpaperBackgroundNode(context: AccountContext, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode {
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
} }

View File

@ -1272,7 +1272,7 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
case let .pattern(data, colors, intensity): case let .pattern(data, colors, intensity):
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size) drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size)
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black) { if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black, true) {
context.setBlendMode(.softLight) context.setBlendMode(.softLight)
context.setAlpha(abs(CGFloat(intensity)) / 100.0) context.setAlpha(abs(CGFloat(intensity)) / 100.0)
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize)) context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))