diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 9222b6a7de..813681aac2 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -84,6 +84,7 @@ 341D392925472F3B00996E7B /* CVViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341D392825472F3B00996E7B /* CVViewState.swift */; }; 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; }; 3420CF6D26327B1A00E7DCB0 /* CVBackgroundContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3420CF6C26327B1A00E7DCB0 /* CVBackgroundContainer.swift */; }; + 3420CF6F2638A01A00E7DCB0 /* CVWallpaperBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */; }; 3421981C21061D2E00C57195 /* ByteParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3421981B21061D2E00C57195 /* ByteParserTest.swift */; }; 34235F3824213550008C74CB /* ConversationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34235F3724213550008C74CB /* ConversationSettingsViewController.swift */; }; 3425A5992631DC05006D5863 /* LinkPreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3425A5982631DC05006D5863 /* LinkPreviewState.swift */; }; @@ -1096,6 +1097,7 @@ 341F2C0D1F2B8AE700D07D6B /* DebugUIMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMisc.h; sourceTree = ""; }; 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = ""; }; 3420CF6C26327B1A00E7DCB0 /* CVBackgroundContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CVBackgroundContainer.swift; sourceTree = ""; }; + 3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CVWallpaperBlurView.swift; sourceTree = ""; }; 3421981B21061D2E00C57195 /* ByteParserTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ByteParserTest.swift; sourceTree = ""; }; 34235F3724213550008C74CB /* ConversationSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationSettingsViewController.swift; sourceTree = ""; }; 3425A5982631DC05006D5863 /* LinkPreviewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkPreviewState.swift; sourceTree = ""; }; @@ -2694,6 +2696,7 @@ 347C381C252CE69400F3D941 /* CVStackView.swift */, 3470C8812559B23F00F5847C /* CVUpdate.swift */, 345772B5257E4E01001D7D44 /* CVViewStateSnapshot.swift */, + 3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */, ); path = CV; sourceTree = ""; @@ -5287,6 +5290,7 @@ 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */, 886BB3D325BA0CA400079781 /* SetWallpaperViewController.swift in Sources */, 887CD47D2473051D00FDD265 /* DeviceTransferService+Manifest.swift in Sources */, + 3420CF6F2638A01A00E7DCB0 /* CVWallpaperBlurView.swift in Sources */, 88D23D2623CEC0C700B0E74B /* CallUIAdapter.swift in Sources */, 34FB6A5325D2D10400E599B1 /* PaymentsViewUtils.swift in Sources */, E44AD4E624E98F440035D7B8 /* PhotoCaptureDismiss.swift in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVCell.swift b/Signal/src/ViewControllers/ConversationView/CV/CVCell.swift index df3a78ce09..7fb6d6c911 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVCell.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVCell.swift @@ -174,24 +174,6 @@ public class CVCell: UICollectionViewCell, CVItemCell, CVRootComponentHost { renderItem: renderItem, messageSwipeActionState: messageSwipeActionState) } - - public func willSnapshotForMessageActions() { - guard let rootComponent = rootComponent, - let componentView = componentView else { - owsFailDebug("Missing component.") - return - } - rootComponent.willSnapshotForMessageActions(componentView: componentView) - } - - public func didSnapshotForMessageActions() { - guard let rootComponent = rootComponent, - let componentView = componentView else { - owsFailDebug("Missing component.") - return - } - rootComponent.didSnapshotForMessageActions(componentView: componentView) - } } // MARK: - @@ -401,12 +383,12 @@ public extension CVRootComponentHost { componentView: componentView) } - func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) { + func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate) { guard let rootComponent = rootComponent, let componentView = componentView else { owsFailDebug("Missing component.") return } - rootComponent.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView) + rootComponent.updateWallpaperBlur(delegate: delegate, componentView: componentView) } } diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponent.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponent.swift index ada43cd206..e7aa2ed9a7 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponent.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponent.swift @@ -55,9 +55,6 @@ public protocol CVComponent: class { func cellDidBecomeVisible(componentView: CVComponentView, renderItem: CVRenderItem, messageSwipeActionState: CVMessageSwipeActionState) - - func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) } // MARK: - @@ -77,9 +74,8 @@ public protocol CVRootComponent: CVComponent { var isDedicatedCell: Bool { get } - func willSnapshotForMessageActions(componentView: CVComponentView) - - func didSnapshotForMessageActions(componentView: CVComponentView) + func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) } // MARK: - @@ -161,14 +157,6 @@ public class CVComponentBase: NSObject { itemModel.itemViewState.isShowingSelectionUI } - public func willSnapshotForMessageActions(componentView: CVComponentView) { - // Do nothing. - } - - public func didSnapshotForMessageActions(componentView: CVComponentView) { - // Do nothing. - } - // MARK: - Root Components public static func configureCellRootComponent(rootComponent: CVRootComponent, @@ -194,8 +182,8 @@ public class CVComponentBase: NSObject { // MARK: - - public func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) { + public func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) { // Do nothing. } } diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentDateHeader.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentDateHeader.swift index c628dc0a75..17e764571b 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentDateHeader.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentDateHeader.swift @@ -41,15 +41,16 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent { CVComponentViewDateHeader() } - public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) { - super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView) + public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) { + super.updateWallpaperBlur(delegate: delegate, + componentView: componentView) guard let componentView = componentView as? CVComponentViewDateHeader else { owsFailDebug("Unexpected componentView.") return } - wallpaperMaskBuilder.append(blurView: componentView.blurView) + componentView.wallpaperBlurView?.configure(delegate: delegate) } public func configureForRendering(componentView: CVComponentView, @@ -87,17 +88,20 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent { innerStack.reset() outerStack.reset() titleLabel.removeFromSuperview() - componentView.blurView?.removeFromSuperview() - componentView.blurView = nil + componentView.wallpaperBlurView?.removeFromSuperview() + componentView.wallpaperBlurView = nil + + if hasWallpaper { + let wallpaperBlurView = CVWallpaperBlurView() + wallpaperBlurView.layer.cornerRadius = 8 + innerStack.addSubviewToFillSuperviewEdges(wallpaperBlurView) + componentView.wallpaperBlurView = wallpaperBlurView + } innerStack.configure(config: innerStackConfig, cellMeasurement: cellMeasurement, measurementKey: Self.measurementKey_innerStack, subviews: [ titleLabel ]) - if hasWallpaper { - innerStack.layer.cornerRadius = 8 - componentView.blurView = innerStack - } outerStack.configure(config: outerStackConfig, cellMeasurement: cellMeasurement, @@ -176,7 +180,7 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent { fileprivate let innerStack = ManualStackView(name: "dateHeader.innerStackView") fileprivate let titleLabel = CVLabel() - fileprivate var blurView: UIView? + fileprivate var wallpaperBlurView: CVWallpaperBlurView? fileprivate var hasWallpaper = false fileprivate var isDarkThemeEnabled = false @@ -203,8 +207,8 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent { titleLabel.removeFromSuperview() - blurView?.removeFromSuperview() - blurView = nil + wallpaperBlurView?.removeFromSuperview() + wallpaperBlurView = nil hasWallpaper = false isDarkThemeEnabled = false diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentSystemMessage.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentSystemMessage.swift index 72ce1ba3cd..59947e9b67 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentSystemMessage.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentSystemMessage.swift @@ -37,22 +37,6 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { componentView: componentView) } - public override func willSnapshotForMessageActions(componentView: CVComponentView) { - guard let componentView = componentView as? CVComponentViewSystemMessage else { - owsFailDebug("Unexpected componentView.") - return - } - componentView.blurView?.backgroundColor = bubbleBackgroundColor - } - - public override func didSnapshotForMessageActions(componentView: CVComponentView) { - guard let componentView = componentView as? CVComponentViewSystemMessage else { - owsFailDebug("Unexpected componentView.") - return - } - componentView.blurView?.backgroundColor = .clear - } - private var bubbleBackgroundColor: UIColor { Theme.backgroundColor } @@ -83,15 +67,16 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { layoutMargins: .zero) } - public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) { - super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView) + public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) { + super.updateWallpaperBlur(delegate: delegate, + componentView: componentView) guard let componentView = componentView as? CVComponentViewSystemMessage else { owsFailDebug("Unexpected componentView.") return } - wallpaperMaskBuilder.append(blurView: componentView.blurView) + componentView.wallpaperBlurView?.configure(delegate: delegate) } public func buildComponentView(componentDelegate: CVComponentDelegate) -> CVComponentView { @@ -212,8 +197,8 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { measurementKey: Self.measurementKey_outerHStack, subviews: outerHStackViews) - componentView.blurView?.removeFromSuperview() - componentView.blurView = nil + componentView.wallpaperBlurView?.removeFromSuperview() + componentView.wallpaperBlurView = nil componentView.backgroundView?.removeFromSuperview() componentView.backgroundView = nil @@ -221,16 +206,15 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { let bubbleView: UIView if hasWallpaper { - // This view doesn't need to render to render blur, - // but it does need to render for the message actions snapshot. - let blurView = UIView.container() - componentView.blurView = blurView - bubbleView = blurView + let wallpaperBlurView = CVWallpaperBlurView() + wallpaperBlurView.layer.cornerRadius = 8 + componentView.wallpaperBlurView = wallpaperBlurView + bubbleView = wallpaperBlurView } else { let backgroundView = UIView() + backgroundView.backgroundColor = Theme.backgroundColor componentView.backgroundView = backgroundView bubbleView = backgroundView - backgroundView.backgroundColor = Theme.backgroundColor } if isFirstInCluster && isLastInCluster { @@ -416,7 +400,7 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { fileprivate let titleLabel = CVLabel() fileprivate let selectionView = MessageSelectionView() - fileprivate var blurView: UIView? + fileprivate var wallpaperBlurView: CVWallpaperBlurView? fileprivate var backgroundView: UIView? fileprivate var button: OWSButton? @@ -453,8 +437,8 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent { innerVStack.reset() outerVStack.reset() - blurView?.removeFromSuperview() - blurView = nil + wallpaperBlurView?.removeFromSuperview() + wallpaperBlurView = nil backgroundView?.removeFromSuperview() backgroundView = nil diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentThreadDetails.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentThreadDetails.swift index 94e71dec8c..e1af735f0d 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentThreadDetails.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentThreadDetails.swift @@ -44,15 +44,16 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { CVComponentViewThreadDetails() } - public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) { - super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView) + public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) { + super.updateWallpaperBlur(delegate: delegate, + componentView: componentView) guard let componentView = componentView as? CVComponentViewThreadDetails else { owsFailDebug("Unexpected componentView.") return } - wallpaperMaskBuilder.append(blurView: componentView.blurView) + componentView.wallpaperBlurView?.configure(delegate: delegate) } public func configureForRendering(componentView componentViewParam: CVComponentView, @@ -117,8 +118,10 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { innerViews.append(UIView.spacer(withHeight: 1)) if conversationStyle.hasWallpaper { - innerStackView.layer.cornerRadius = 12 - componentView.blurView = innerStackView + let wallpaperBlurView = CVWallpaperBlurView() + wallpaperBlurView.layer.cornerRadius = 12 + innerStackView.addSubviewToFillSuperviewEdges(wallpaperBlurView) + componentView.wallpaperBlurView = wallpaperBlurView } let titleLabel = componentView.titleLabel @@ -514,7 +517,7 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { fileprivate let outerStackView = ManualStackView(name: "Thread details outer") fileprivate let innerStackView = ManualStackView(name: "Thread details inner") - fileprivate var blurView: UIView? + fileprivate var wallpaperBlurView: CVWallpaperBlurView? public var isDedicatedCellView = false @@ -536,8 +539,8 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { mutualGroupsLabel.text = nil avatarView = nil - blurView?.removeFromSuperview() - blurView = nil + wallpaperBlurView?.removeFromSuperview() + wallpaperBlurView = nil } } } diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentUnreadIndicator.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentUnreadIndicator.swift index 615d8b1a41..2699c2fa97 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentUnreadIndicator.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponents/CVComponentUnreadIndicator.swift @@ -34,15 +34,16 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent { CVComponentViewUnreadIndicator() } - public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder, - componentView: CVComponentView) { - super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView) + public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate, + componentView: CVComponentView) { + super.updateWallpaperBlur(delegate: delegate, + componentView: componentView) guard let componentView = componentView as? CVComponentViewUnreadIndicator else { owsFailDebug("Unexpected componentView.") return } - wallpaperMaskBuilder.append(blurView: componentView.blurView) + componentView.wallpaperBlurView?.configure(delegate: delegate) } public func configureForRendering(componentView: CVComponentView, @@ -79,8 +80,8 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent { } else { outerStack.reset() titleLabel.removeFromSuperview() - componentView.blurView?.removeFromSuperview() - componentView.blurView = nil + componentView.wallpaperBlurView?.removeFromSuperview() + componentView.wallpaperBlurView = nil innerStack.reset() innerStack.configure(config: innerStackConfig, @@ -91,9 +92,10 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent { if hasWallpaper { strokeView.backgroundColor = .ows_blackAlpha80 - let blurView = innerStack - blurView.layer.cornerRadius = 8 - componentView.blurView = blurView + let wallpaperBlurView = CVWallpaperBlurView() + wallpaperBlurView.layer.cornerRadius = 8 + innerStack.addSubviewToFillSuperviewEdges(wallpaperBlurView) + componentView.wallpaperBlurView = wallpaperBlurView } else { strokeView.backgroundColor = .ows_gray45 } @@ -175,7 +177,7 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent { fileprivate let titleLabel = CVLabel() - fileprivate var blurView: UIView? + fileprivate var wallpaperBlurView: CVWallpaperBlurView? fileprivate var hasWallpaper = false fileprivate var isDarkThemeEnabled = false @@ -201,8 +203,8 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent { outerStack.reset() innerStack.reset() - blurView?.removeFromSuperview() - blurView = nil + wallpaperBlurView?.removeFromSuperview() + wallpaperBlurView = nil hasWallpaper = false isDarkThemeEnabled = false diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVWallpaperBlurView.swift b/Signal/src/ViewControllers/ConversationView/CV/CVWallpaperBlurView.swift new file mode 100644 index 0000000000..df5cadc236 --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/CV/CVWallpaperBlurView.swift @@ -0,0 +1,153 @@ +// +// Copyright (c) 2021 Open Whisper Systems. All rights reserved. +// + +import Foundation + +public protocol CVWallpaperBlurDelegate: class { + var wallpaperBlurState: WallpaperBlurState? { get } + var isAnimating: Bool { get } +} + +// MARK: - + +@objc +public class CVWallpaperBlurView: ManualLayoutView { + + private weak var delegate: CVWallpaperBlurDelegate? + + private let imageView = CVImageView() +// private let imageLayer = CALayer() + private let maskLayer = CAShapeLayer() + + private var state: WallpaperBlurState? + + required init() { + super.init(name: "CVWallpaperBlurView") + + self.clipsToBounds = true + + imageView.contentMode = .scaleAspectFill + imageView.layer.mask = maskLayer + imageView.layer.masksToBounds = true + addSubview(imageView) + +// addSubview(imageView) { view in +// guard let view = view as? CVWallpaperBlurView else { +// owsFailDebug("Invalid view.") +// return +// } +// view.imageView.frame = view.imageViewFrame +// } +// self.layer.addSublayer(imageLayer) +// self.layer.masksToBounds = true +// imageLayer.contentsGravity = .resizeAspectFill +// imageLayer.mask = maskLayer +// self.layer.mask = maskLayer + + addLayoutBlock { [weak self] _ in + self?.applyLayout() + } + addRedBorder() +// addLayoutBlock { view in +// guard let view = view as? CVWallpaperBlurView else { +// owsFailDebug("Invalid view.") +// return +// } +// view.imageLayer.frame = view.imageViewFrame +// } + } + + private func applyLayout() { + imageView.frame = imageViewFrame +// imageLayer.frame = imageViewFrame + + maskLayer.frame = imageView.bounds + let maskPath = UIBezierPath(roundedRect: maskFrame, cornerRadius: layer.cornerRadius) + maskLayer.path = maskPath.cgPath + } + + @available(swift, obsoleted: 1.0) + required init(name: String) { + owsFail("Do not use this initializer.") + } + + public func configure(delegate: CVWallpaperBlurDelegate) { + // TODO: Observe provider changes. + self.delegate = delegate + + configure() + } + + private func configure() { + guard let delegate = delegate else { + owsFailDebug("Missing delegate.") + resetContent() + return + } + guard let state = delegate.wallpaperBlurState else { + resetContent() + return + } + guard state.id != self.state?.id else { + ensurePositioning() + return + } + self.state = state + imageView.image = state.image +// imageLayer.contents = state.image.cgImage + + ensurePositioning() + } + + private var imageViewFrame: CGRect = .zero + private var maskFrame: CGRect = .zero + + private func ensurePositioning() { +// guard let delegate = delegate else { +// owsFailDebug("Missing delegate.") +// resetContent() +// return +// } + guard let state = self.state else { + resetContent() + return + } +// guard let image = imageView.image else { +// owsFailDebug("Missing image.") +// resetContent() +// return +// } + let referenceView = state.referenceView + self.imageViewFrame = self.convert(referenceView.bounds, from: referenceView) + self.maskFrame = referenceView.convert(self.bounds, from: self) + +// let widthFactor = referenceView.bounds.width / image.size.width +// let heightFactor = referenceView.bounds.height / image.size.height +// let scalingFactor = max(widthFactor, heightFactor) +// let contentsScale = 1 / scalingFactor +// Logger.verbose("---- scalingFactor: \(scalingFactor), contentsScale: \(layer.contentsScale), UIScreen.main.scale: \(UIScreen.main.scale)") +//// self.layer.contentsScale +// // https://developer.apple.com/documentation/quartzcore/calayer/1410746-contentsscale +// // By using contentsScale to scale the blurred background image, we not only +// // (increase) the scale so that it is rendered at the correct size, we also +// // reduce the size of the bitmap used to present the layer content, which +// // helps perf. +// self.layer.contentsScale = contentsScale +// let referencePoint = self.convert(referenceView.bounds.center, from: referenceView) +// Logger.verbose("---- contentsCenter: \(layer.contentsCenter), position: \(layer.position), anchorPoint: \(layer.anchorPoint), referencePoint: \(referencePoint)") + Logger.verbose("---- imageViewFrame: \(imageViewFrame)") + Logger.verbose("---- maskFrame: \(maskFrame)") +// self.layer.contentsCenter = CGRect(origin: .zero, size: <#T##CGSize#>) referencePoint +// imageView.frame = imageViewFrame + applyLayout() + } + + private func resetContent() { + imageView.image = nil +// imageLayer.contents = nil + imageViewFrame = .zero + maskFrame = .zero + delegate = nil + } +} diff --git a/Signal/src/ViewControllers/ConversationView/CVBackgroundContainer.swift b/Signal/src/ViewControllers/ConversationView/CVBackgroundContainer.swift index 2248ce2f30..2e45fe644b 100644 --- a/Signal/src/ViewControllers/ConversationView/CVBackgroundContainer.swift +++ b/Signal/src/ViewControllers/ConversationView/CVBackgroundContainer.swift @@ -8,6 +8,7 @@ import Foundation public protocol CVBackgroundContainerDelegate: class { func updateSelectionHighlight() func updateScrollingContent() + func updateCellWallpaperBlur() } // MARK: - @@ -19,7 +20,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer { case wallpaperContent = 0 case wallpaperDimming = 1 case selectionHighlight = 2 - case wallpaperBlur = 3 } fileprivate var wallpaperView: WallpaperView? @@ -55,7 +55,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer { } public func set(wallpaperView: WallpaperView?) { - self.wallpaperView?.blurView?.removeFromSuperview() self.wallpaperView?.contentView?.removeFromSuperview() self.wallpaperView?.dimmingView?.removeFromSuperview() self.wallpaperView = wallpaperView @@ -71,10 +70,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer { addSubview(dimmingView) dimmingView.layer.zPosition = ZPositioning.wallpaperDimming.rawValue } - if let blurView = wallpaperView.blurView { - addSubview(blurView) - blurView.layer.zPosition = ZPositioning.wallpaperBlur.rawValue - } setNeedsLayout() } else { @@ -87,17 +82,13 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer { super.layoutSubviews() - let shouldUpdateWallpaperBlur = wallpaperView?.blurView?.frame != bounds let shouldUpdateSelectionHighlight = selectionHighlightView.frame != bounds - wallpaperView?.blurView?.frame = bounds wallpaperView?.contentView?.frame = bounds wallpaperView?.dimmingView?.frame = bounds selectionHighlightView.frame = bounds - if shouldUpdateWallpaperBlur { - wallpaperView?.updateBlurContentAndMask() - } + delegate?.updateCellWallpaperBlur() if shouldUpdateSelectionHighlight { delegate?.updateSelectionHighlight() } @@ -108,12 +99,11 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer { fileprivate extension CVBackgroundContainer { - func updateWallpaperBlurMask() { - AssertIsOnMainThread() - - let isAnimating = shouldHaveUpdateScrollingContentTimer.get() > 0 - wallpaperView?.updateBlurMask(isAnimating: isAnimating) - } +// func updateWallpaperBlur(_ delegate: CVWallpaperBlurDelegate) { +// AssertIsOnMainThread() +// +// updateCellWallpaperBlur() +// } func updateScrollingContentForAnimation(duration: TimeInterval) { AssertIsOnMainThread() @@ -121,7 +111,7 @@ fileprivate extension CVBackgroundContainer { // To ensure a "smooth landing" of these animations, we need to continue // to update the mask for a short period after the animations lands. let duration = duration * 2 - + startUpdateScrollingContentTimer() DispatchQueue.main.asyncAfter(deadline: .now() + duration) { [weak self] in self?.stopUpdateScrollingContentTimer() @@ -164,37 +154,60 @@ fileprivate extension CVBackgroundContainer { // MARK: - +extension CVBackgroundContainer: CVWallpaperBlurDelegate { + public var wallpaperBlurState: WallpaperBlurState? { + wallpaperView?.blurProvider?.getState() + } + + public var isAnimating: Bool { + shouldHaveUpdateScrollingContentTimer.get() > 0 + } +} + +// MARK: - + extension ConversationViewController: CVBackgroundContainerDelegate { var selectionHighlightView: SelectionHighlightView { - viewState.backgroundContainer.selectionHighlightView + backgroundContainer.selectionHighlightView } @objc public func updateScrollingContent() { AssertIsOnMainThread() - viewState.backgroundContainer.updateWallpaperBlurMask() + updateCellWallpaperBlur() updateSelectionHighlight() } + public func updateCellWallpaperBlur() { + let provider = backgroundContainer.wallpaperView?.blurProvider + for cell in collectionView.visibleCells { + guard let cell = cell as? CVCell else { + owsFailDebug("Invalid cell.") + continue + } + cell.updateWallpaperBlur(delegate: backgroundContainer) + } + } + @objc public func updateScrollingContentForAnimation(duration: TimeInterval) { AssertIsOnMainThread() - viewState.backgroundContainer.updateScrollingContentForAnimation(duration: duration) + backgroundContainer.updateScrollingContentForAnimation(duration: duration) } @objc public func startUpdateScrollingContentTimer() { AssertIsOnMainThread() - viewState.backgroundContainer.startUpdateScrollingContentTimer() + backgroundContainer.startUpdateScrollingContentTimer() } @objc public func stopUpdateScrollingContentTimer() { AssertIsOnMainThread() - viewState.backgroundContainer.stopUpdateScrollingContentTimer() + backgroundContainer.stopUpdateScrollingContentTimer() } } diff --git a/Signal/src/ViewControllers/ConversationView/CVViewState.swift b/Signal/src/ViewControllers/ConversationView/CVViewState.swift index 11c294c866..596a1438f3 100644 --- a/Signal/src/ViewControllers/ConversationView/CVViewState.swift +++ b/Signal/src/ViewControllers/ConversationView/CVViewState.swift @@ -273,6 +273,9 @@ public extension ConversationViewController { get { viewState.messageActionsOriginalFocusY } set { viewState.messageActionsOriginalFocusY = newValue } } + var backgroundContainer: CVBackgroundContainer { + get { viewState.backgroundContainer } + } // MARK: - diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController+Wallpaper.swift b/Signal/src/ViewControllers/ConversationView/ConversationViewController+Wallpaper.swift index dd62c3dbd8..200e5ae8de 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController+Wallpaper.swift +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController+Wallpaper.swift @@ -33,30 +33,12 @@ extension ConversationViewController { AssertIsOnMainThread() guard let wallpaperView = databaseStorage.read(block: { transaction in - Wallpaper.view(for: self.thread, - maskDataSource: self, - transaction: transaction) + Wallpaper.view(for: self.thread, transaction: transaction) }) else { - viewState.backgroundContainer.set(wallpaperView: nil) + backgroundContainer.set(wallpaperView: nil) return } - viewState.backgroundContainer.set(wallpaperView: wallpaperView) + backgroundContainer.set(wallpaperView: wallpaperView) } } - -// MARK: - - -extension ConversationViewController: WallpaperMaskDataSource { - public func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) { - for cell in collectionView.visibleCells { - guard let cell = cell as? CVCell else { - owsFailDebug("Invalid cell.") - continue - } - cell.buildWallpaperMask(wallpaperMaskBuilder) - } - } - - public var isWallpaperPreview: Bool { false } -} diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index b39fa64330..ca11f421ed 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -478,7 +478,7 @@ typedef enum : NSUInteger { // The view controller will only automatically adjust content insets for a // scrollView at index 0, so we need the collection view to remain subview index 0. // But the background views should appear visually behind the collection view. - CVBackgroundContainer *backgroundContainer = self.viewState.backgroundContainer; + CVBackgroundContainer *backgroundContainer = self.backgroundContainer; backgroundContainer.delegate = self; [self.view addSubview:backgroundContainer]; [backgroundContainer autoPinEdgesToSuperviewEdges]; diff --git a/Signal/src/ViewControllers/MessageActionsViewController.swift b/Signal/src/ViewControllers/MessageActionsViewController.swift index 329cb0aa45..6cdce25cd0 100644 --- a/Signal/src/ViewControllers/MessageActionsViewController.swift +++ b/Signal/src/ViewControllers/MessageActionsViewController.swift @@ -126,14 +126,6 @@ public class MessageActionsViewController: UIViewController { snapshotFocusedView?.removeFromSuperview() snapshotFocusedView = nil - if let cell = focusedView as? CVCell { - cell.willSnapshotForMessageActions() - } - defer { - if let cell = focusedView as? CVCell { - cell.didSnapshotForMessageActions() - } - } guard let snapshotView = focusedView.snapshotView(afterScreenUpdates: true) else { return owsFailDebug("snapshotView was unexpectedly nil") } diff --git a/Signal/src/ViewControllers/Wallpapers/PreviewWallpaperViewController.swift b/Signal/src/ViewControllers/Wallpapers/PreviewWallpaperViewController.swift index 714613be43..385a42a538 100644 --- a/Signal/src/ViewControllers/Wallpapers/PreviewWallpaperViewController.swift +++ b/Signal/src/ViewControllers/Wallpapers/PreviewWallpaperViewController.swift @@ -147,7 +147,6 @@ class PreviewWallpaperViewController: UIViewController { case .photo(let selectedPhoto): owsAssertDebug(self.standalonePage == nil) let standalonePage = WallpaperPage(wallpaper: .photo, - maskDataSource: mockConversationView, thread: thread, photo: selectedPhoto) self.standalonePage = standalonePage @@ -165,7 +164,6 @@ class PreviewWallpaperViewController: UIViewController { } currentPage = WallpaperPage(wallpaper: selectedWallpaper, - maskDataSource: mockConversationView, thread: thread) blurButton.isHidden = true } @@ -218,14 +216,12 @@ extension PreviewWallpaperViewController: UIPageViewControllerDataSource, UIPage func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let currentPage = currentPage, currentPage.wallpaper != .photo else { return nil } return WallpaperPage(wallpaper: wallpaper(before: currentPage.wallpaper), - maskDataSource: mockConversationView, thread: thread) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let currentPage = currentPage, currentPage.wallpaper != .photo else { return nil } return WallpaperPage(wallpaper: wallpaper(after: currentPage.wallpaper), - maskDataSource: mockConversationView, thread: thread) } @@ -290,14 +286,10 @@ private class WallpaperPage: UIViewController { let photo: UIImage? var shouldBlur = false { didSet { updatePhoto() } } - private weak var maskDataSource: WallpaperMaskDataSource? - init(wallpaper: Wallpaper, - maskDataSource: WallpaperMaskDataSource, thread: TSThread?, photo: UIImage? = nil) { self.wallpaper = wallpaper - self.maskDataSource = maskDataSource self.thread = thread self.photo = photo @@ -315,36 +307,27 @@ private class WallpaperPage: UIViewController { var wallpaperViewHeightAndWidthPriorityConstraints = [NSLayoutConstraint]() var wallpaperView: WallpaperView? - var wallpaperContentPreviewView: UIView? - var wallpaperBlurPreviewView: UIView? + var wallpaperPreviewView: UIView? override func loadView() { let rootView = ManualLayoutViewWithLayer(name: "rootView") rootView.shouldDeactivateConstraints = false rootView.translatesAutoresizingMaskIntoConstraints = true rootView.backgroundColor = Theme.darkThemeBackgroundColor - rootView.addLayoutBlock { [weak self] _ in - guard let self = self else { return } - self.wallpaperView?.updateBlurContentAndMask() - } view = rootView let shouldDim = databaseStorage.read { transaction in Wallpaper.shouldDim(thread: thread, transaction: transaction) } guard let wallpaperView = Wallpaper.view(for: wallpaper, - maskDataSource: maskDataSource, photo: photo, shouldDim: shouldDim) else { return owsFailDebug("Failed to create photo wallpaper view") } self.wallpaperView = wallpaperView - let wallpaperContentPreviewView = wallpaperView.asPreviewView(mode: .contentAndDimming) - self.wallpaperContentPreviewView = wallpaperContentPreviewView - - let wallpaperBlurPreviewView = wallpaperView.asPreviewView(mode: .blur) - self.wallpaperBlurPreviewView = wallpaperBlurPreviewView + let wallpaperPreviewView = wallpaperView.asPreviewView() + self.wallpaperPreviewView = wallpaperPreviewView // If this is a photo, embed it in a scrollView for pinch & zoom if case .photo = wallpaper, let photo = photo { @@ -355,17 +338,17 @@ private class WallpaperPage: UIViewController { scrollView.delegate = self view.addSubview(scrollView) scrollView.autoPinEdgesToSuperviewEdges() - scrollView.addSubview(wallpaperContentPreviewView) + scrollView.addSubview(wallpaperPreviewView) - wallpaperContentPreviewView.autoPinEdgesToSuperviewEdges() + wallpaperPreviewView.autoPinEdgesToSuperviewEdges() wallpaperViewWidthPriorityConstraints = [ - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .width, to: .width, of: scrollView ), - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .height, to: .width, of: scrollView, @@ -375,12 +358,12 @@ private class WallpaperPage: UIViewController { wallpaperViewWidthPriorityConstraints.forEach { $0.isActive = false } wallpaperViewHeightPriorityConstraints = [ - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .height, to: .height, of: scrollView ), - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .width, to: .height, of: scrollView, @@ -390,12 +373,12 @@ private class WallpaperPage: UIViewController { wallpaperViewHeightPriorityConstraints.forEach { $0.isActive = false } wallpaperViewHeightAndWidthPriorityConstraints = [ - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .height, to: .height, of: scrollView ), - wallpaperContentPreviewView.autoMatch( + wallpaperPreviewView.autoMatch( .width, to: .width, of: scrollView @@ -405,24 +388,9 @@ private class WallpaperPage: UIViewController { updateWallpaperConstraints(reference: view.bounds.size) } else { - view.addSubview(wallpaperContentPreviewView) - wallpaperContentPreviewView.autoPinEdgesToSuperviewEdges() + view.addSubview(wallpaperPreviewView) + wallpaperPreviewView.autoPinEdgesToSuperviewEdges() } - - view.addSubview(wallpaperBlurPreviewView) - wallpaperBlurPreviewView.autoPinEdgesToSuperviewEdges() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - self.wallpaperView?.updateBlurContentAndMask() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - self.wallpaperView?.updateBlurContentAndMask() } private func updatePhoto() { @@ -491,7 +459,7 @@ private class WallpaperPage: UIViewController { extension WallpaperPage: UIScrollViewDelegate { func viewForZooming(in scrollView: UIScrollView) -> UIView? { - return wallpaperContentPreviewView + return wallpaperPreviewView } } diff --git a/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift b/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift index f9559e43e2..5f156f1c1f 100644 --- a/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift +++ b/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift @@ -242,8 +242,7 @@ class WallpaperCell: UICollectionViewCell { Wallpaper.shouldDim(thread: thread, transaction: transaction) } wallpaperView = Wallpaper.view(for: wallpaper, - maskDataSource: nil, - shouldDim: shouldDim)?.asPreviewView(mode: .all) + shouldDim: shouldDim)?.asPreviewView() guard let wallpaperView = wallpaperView else { return owsFailDebug("Missing wallpaper view") diff --git a/Signal/src/ViewControllers/Wallpapers/WallpaperSettingsViewController.swift b/Signal/src/ViewControllers/Wallpapers/WallpaperSettingsViewController.swift index f4d4a184b1..c6178fed54 100644 --- a/Signal/src/ViewControllers/Wallpapers/WallpaperSettingsViewController.swift +++ b/Signal/src/ViewControllers/Wallpapers/WallpaperSettingsViewController.swift @@ -202,12 +202,10 @@ class MiniPreviewView: UIView { backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray05 let stackViewContainer: UIView - if let wallpaperView = databaseStorage.read( - block: { Wallpaper.view(for: thread, - maskDataSource: nil, - transaction: $0) } - ) { - stackViewContainer = wallpaperView.asPreviewView(mode: .all) + if let wallpaperView = (databaseStorage.read { transaction in + Wallpaper.view(for: thread, transaction: transaction) + }) { + stackViewContainer = wallpaperView.asPreviewView() } else { stackViewContainer = UIView() stackViewContainer.backgroundColor = Theme.backgroundColor diff --git a/Signal/src/views/MockConversationView.swift b/Signal/src/views/MockConversationView.swift index a5721d9b9a..14dd5973d9 100644 --- a/Signal/src/views/MockConversationView.swift +++ b/Signal/src/views/MockConversationView.swift @@ -197,15 +197,15 @@ class MockConversationView: UIView { // MARK: - -extension MockConversationView: WallpaperMaskDataSource { - func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) { - outgoingMessageView.buildWallpaperMask(wallpaperMaskBuilder) - incomingMessageView.buildWallpaperMask(wallpaperMaskBuilder) - dateHeaderView.buildWallpaperMask(wallpaperMaskBuilder) - } - - var isWallpaperPreview: Bool { true } -} +// extension MockConversationView: WallpaperMaskDataSource { +// func updateWallpaperBlur(_ delegate: CVWallpaperBlurDelegate) { +// outgoingMessageView.buildWallpaperMask(delegate) +// incomingMessageView.buildWallpaperMask(delegate) +// dateHeaderView.buildWallpaperMask(delegate) +// } +// +// var isWallpaperPreview: Bool { true } +// } // MARK: - Mock Classes diff --git a/SignalMessaging/Wallpapers/Wallpaper.swift b/SignalMessaging/Wallpapers/Wallpaper.swift index 16a8acf757..bfdae9a984 100644 --- a/SignalMessaging/Wallpapers/Wallpaper.swift +++ b/SignalMessaging/Wallpapers/Wallpaper.swift @@ -142,7 +142,6 @@ public enum Wallpaper: String, CaseIterable { } public static func view(for thread: TSThread? = nil, - maskDataSource: WallpaperMaskDataSource?, transaction: SDSAnyReadTransaction) -> WallpaperView? { AssertIsOnMainThread() @@ -176,7 +175,6 @@ public enum Wallpaper: String, CaseIterable { dimInDarkMode(for: thread, transaction: transaction)) guard let view = view(for: wallpaper, - maskDataSource: maskDataSource, photo: photo, shouldDim: shouldDim) else { return nil @@ -192,7 +190,6 @@ public enum Wallpaper: String, CaseIterable { } public static func view(for wallpaper: Wallpaper, - maskDataSource: WallpaperMaskDataSource?, photo: UIImage? = nil, shouldDim: Bool) -> WallpaperView? { AssertIsOnMainThread() @@ -215,9 +212,7 @@ public enum Wallpaper: String, CaseIterable { }() else { return nil } - return WallpaperView(mode: mode, - maskDataSource: maskDataSource, - shouldDim: shouldDim) + return WallpaperView(mode: mode, shouldDim: shouldDim) } } @@ -356,49 +351,6 @@ fileprivate extension Wallpaper { // MARK: - -public struct WallpaperMaskBuilder { - - fileprivate let maskPath = UIBezierPath() - public let referenceView: UIView - fileprivate let isAnimating: Bool - - public func append(blurPath: UIBezierPath) { - maskPath.append(blurPath) - } - - public func append(blurView: UIView?) { - guard let blurView = blurView else { - Logger.warn("Missing blurView.") - return - } - - let blurFrame: CGRect - if isAnimating, - let srcLayer = blurView.layer.presentation(), - let dstLayer = referenceView.layer.presentation() { - blurFrame = dstLayer.convert(srcLayer.bounds, from: srcLayer) - } else { - blurFrame = referenceView.convert(blurView.bounds, from: blurView) - } - - let blurPath: UIBezierPath = { - return UIBezierPath(roundedRect: blurFrame, - byRoundingCorners: blurView.layer.maskedCorners.asUIRectCorner, - cornerRadii: .square(blurView.layer.cornerRadius)) - }() - append(blurPath: blurPath) - } -} - -// MARK: - - -public protocol WallpaperMaskDataSource: class { - func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) - var isWallpaperPreview: Bool { get } -} - -// MARK: - - public class WallpaperView { fileprivate enum Mode { case solidColor(solidColor: UIColor) @@ -406,22 +358,18 @@ public class WallpaperView { case image(image: UIImage) } - private var _blurView: BlurView? - - public var blurView: UIView? { _blurView } - public private(set) var contentView: UIView? public private(set) var dimmingView: UIView? + public private(set) var blurProvider: WallpaperBlurProvider? + private let mode: Mode - fileprivate init(mode: Mode, - maskDataSource: WallpaperMaskDataSource?, - shouldDim: Bool) { + fileprivate init(mode: Mode, shouldDim: Bool) { self.mode = mode - configure(maskDataSource: maskDataSource, shouldDim: shouldDim) + configure(shouldDim: shouldDim) } @available(swift, obsoleted: 1.0) @@ -429,213 +377,167 @@ public class WallpaperView { owsFail("Do not use this initializer.") } - public enum PreviewMode { - case all - case blur - case contentAndDimming - - fileprivate var showContentAndDimming: Bool { - switch self { - case .all, .contentAndDimming: - return true - case .blur: - return false - } - } - - fileprivate var showBlur: Bool { - switch self { - case .all, .contentAndDimming: - return false - case .blur: - return true - } - } - } - - public func asPreviewView(mode: PreviewMode) -> UIView { + public func asPreviewView() -> UIView { let previewView = UIView.container() - if mode.showContentAndDimming, - let contentView = self.contentView { + if let contentView = self.contentView { previewView.addSubview(contentView) contentView.autoPinEdgesToSuperviewEdges() } - if mode.showContentAndDimming, - let dimmingView = self.dimmingView { + if let dimmingView = self.dimmingView { previewView.addSubview(dimmingView) dimmingView.autoPinEdgesToSuperviewEdges() } - if mode.showBlur, - let blurView = self.blurView { - previewView.addSubview(blurView) - blurView.autoPinEdgesToSuperviewEdges() - } return previewView } - private func configure(maskDataSource: WallpaperMaskDataSource?, - shouldDim: Bool) { + private func configure(shouldDim: Bool) { + func addDimmingViewIfNecessary() { + if shouldDim { + let dimmingView = UIView() + dimmingView.backgroundColor = .ows_blackAlpha20 + self.dimmingView = dimmingView + } + } + let contentView: UIView = { switch mode { case .solidColor(let solidColor): - let isWallpaperPreview = maskDataSource?.isWallpaperPreview ?? false - if isWallpaperPreview { - let contentView = OWSLayerView(frame: .zero) { [weak self] _ in - self?.updateBlurContentAndMask() - } - contentView.backgroundColor = solidColor - return contentView - } else { - let contentView = UIView() - contentView.backgroundColor = solidColor - return contentView - } + // Bake the dimming into the color. + let color = (shouldDim + ? solidColor.blended(with: .ows_black, alpha: 0.2) + : solidColor) + + let contentView = UIView() + contentView.backgroundColor = color + return contentView case .gradientView(let gradientView): + + addDimmingViewIfNecessary() + return gradientView case .image(let image): let imageView = UIImageView(image: image) imageView.contentMode = .scaleAspectFill imageView.clipsToBounds = true + + // TODO: Bake dimming into the image. + addDimmingViewIfNecessary() + return imageView } }() self.contentView = contentView - addBlurView(contentView: contentView, maskDataSource: maskDataSource) - - if shouldDim { - let dimmingView = UIView() - dimmingView.backgroundColor = .ows_blackAlpha20 - self.dimmingView = dimmingView - } + addBlurProvider(contentView: contentView) } - private func addBlurView(contentView: UIView, - maskDataSource: WallpaperMaskDataSource?) { - guard let maskDataSource = maskDataSource else { - return - } - let blurView = BlurView(contentView: contentView, maskDataSource: maskDataSource) - _blurView = blurView + private func addBlurProvider(contentView: UIView) { + self.blurProvider = WallpaperBlurProvider(contentView: contentView) } - public func updateBlurContentAndMask(isAnimating: Bool = false) { - _blurView?.updateContentAndMask(isAnimating: isAnimating) +// public func updateBlurContentAndMask(isAnimating: Bool = false) { +// _blurView?.updateContentAndMask(isAnimating: isAnimating) +// } +// +// public func updateBlurMask(isAnimating: Bool = false) { +// _blurView?.updateMask(isAnimating: isAnimating) +// } +} + +// MARK: - + +private struct WallpaperBlurToken: Equatable { + let contentSize: CGSize + let isDarkThemeEnabled: Bool +} + +// MARK: - + +public class WallpaperBlurState { + public let image: UIImage + public let referenceView: UIView + fileprivate let token: WallpaperBlurToken + + private static let idCounter = AtomicUInt(0) + public let id: UInt = WallpaperBlurState.idCounter.increment() + + fileprivate init(image: UIImage, + referenceView: UIView, + token: WallpaperBlurToken) { + self.image = image + self.referenceView = referenceView + self.token = token + } +} + +// MARK: - + +public class WallpaperBlurProvider { + private let contentView: UIView + + private var state: WallpaperBlurState? + + init(contentView: UIView) { + self.contentView = contentView } - public func updateBlurMask(isAnimating: Bool = false) { - _blurView?.updateMask(isAnimating: isAnimating) + @available(swift, obsoleted: 1.0) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") } - // MARK: - + public func reset() { + self.state = nil + } - private class BlurView: UIImageView { - private let contentView: UIView - private let maskLayer = CAShapeLayer() - private weak var maskDataSource: WallpaperMaskDataSource? + public func getState() -> WallpaperBlurState? { + AssertIsOnMainThread() - init(contentView: UIView, maskDataSource: WallpaperMaskDataSource) { - self.contentView = contentView - self.maskDataSource = maskDataSource - - super.init(frame: .zero) - - self.contentMode = .scaleAspectFill - self.clipsToBounds = true - - self.layer.mask = maskLayer - - NotificationCenter.default.addObserver(self, - selector: #selector(themeDidChange), - name: .ThemeDidChange, - object: nil) + // De-bounce. + let bounds = contentView.bounds + let isDarkThemeEnabled = Theme.isDarkThemeEnabled + let newToken = WallpaperBlurToken(contentSize: bounds.size, + isDarkThemeEnabled: isDarkThemeEnabled) + if let state = self.state, + state.token == newToken { + return state } - @available(swift, obsoleted: 1.0) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + reset() - @objc - private func themeDidChange() { - updateContent() - } - - func updateContentAndMask(isAnimating: Bool) { - updateContent() - updateMask(isAnimating: isAnimating) - } - - func updateMask(isAnimating: Bool) { - guard let maskDataSource = self.maskDataSource else { - owsFailDebug("Missing maskDataSource.") - resetMask() - return + do { + guard bounds.width > 0, bounds.height > 0 else { + return nil } - let builder = WallpaperMaskBuilder(referenceView: self, isAnimating: isAnimating) - maskDataSource.buildWallpaperMask(builder) - maskLayer.path = builder.maskPath.cgPath - } - - private struct ContentToken: Equatable { - let contentSize: CGSize - let isDarkThemeEnabled: Bool - } - private var contentToken: ContentToken? - - func updateContent() { - // De-bounce. - let isDarkThemeEnabled = Theme.isDarkThemeEnabled - let newContentToken = ContentToken(contentSize: bounds.size, - isDarkThemeEnabled: isDarkThemeEnabled) - guard contentToken != newContentToken else { - return + guard let contentImage = contentView.renderAsImage() else { + owsFailDebug("Could not render contentView.") + return nil } - - do { - guard bounds.width > 0, bounds.height > 0 else { - resetContent() - return - } - guard let contentImage = contentView.renderAsImage() else { - owsFailDebug("Could not render contentView.") - resetContent() - return - } - // We approximate the behavior of UIVisualEffectView(effect: UIBlurEffect(style: .regular)). - let tintColor: UIColor = (isDarkThemeEnabled - ? UIColor.ows_black.withAlphaComponent(0.9) - : .ows_whiteAlpha60) - let resizeFactor: CGFloat = 8 - let resizeDimension = contentImage.size.largerAxis / resizeFactor - guard let scaledImage = contentImage.resized(withMaxDimensionPoints: resizeDimension) else { - owsFailDebug("Could not resize contentImage.") - resetContent() - return - } - let blurRadius: CGFloat = 32 / resizeFactor - let blurredImage = try scaledImage.withGausianBlur(radius: blurRadius, - tintColor: tintColor) - self.image = blurredImage - self.contentToken = newContentToken - } catch { - owsFailDebug("Error: \(error).") - resetContent() + // We approximate the behavior of UIVisualEffectView(effect: UIBlurEffect(style: .regular)). +// let tintColor: UIColor = (isDarkThemeEnabled +// ? UIColor.ows_black.withAlphaComponent(0.9) +// : UIColor.white.withAlphaComponent(0.6) + let tintColor: UIColor = (isDarkThemeEnabled + ? UIColor.ows_black.withAlphaComponent(0.3) + : UIColor.white.withAlphaComponent(0.3)) + let resizeFactor: CGFloat = 8 + let resizeDimension = contentImage.size.largerAxis / resizeFactor + guard let scaledImage = contentImage.resized(withMaxDimensionPoints: resizeDimension) else { + owsFailDebug("Could not resize contentImage.") + return nil } - } - - private func reset() { - resetContent() - resetMask() - } - - private func resetMask() { - maskLayer.path = nil - } - - private func resetContent() { - image = nil - contentToken = nil + let blurRadius: CGFloat = 1 / resizeFactor +// let blurRadius: CGFloat = 32 / resizeFactor + let blurredImage = try scaledImage.withGausianBlur(radius: blurRadius, + tintColor: tintColor) + let state = WallpaperBlurState(image: blurredImage, + referenceView: contentView, + token: newToken) + self.state = state + return state + } catch { + owsFailDebug("Error: \(error).") + return nil } } } diff --git a/SignalServiceKit/src/Util/FeatureFlags.swift b/SignalServiceKit/src/Util/FeatureFlags.swift index fbd16df14a..f2b6cc8da7 100644 --- a/SignalServiceKit/src/Util/FeatureFlags.swift +++ b/SignalServiceKit/src/Util/FeatureFlags.swift @@ -231,7 +231,7 @@ public class DebugFlags: BaseFlags { public static let suppressBackgroundActivity = false @objc - public static let reduceLogChatter = false && build.includes(.dev) + public static let reduceLogChatter = true && build.includes(.dev) @objc public static let logSQLQueries = build.includes(.dev) && !reduceLogChatter