mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add shared media capture protection
This commit is contained in:
parent
c00520929c
commit
3c46e77712
@ -15,6 +15,9 @@ private let nullAction = NullActionClass()
|
|||||||
public protocol SparseItemGridLayer: CALayer {
|
public protocol SparseItemGridLayer: CALayer {
|
||||||
func update(size: CGSize)
|
func update(size: CGSize)
|
||||||
func needsShimmer() -> Bool
|
func needsShimmer() -> Bool
|
||||||
|
|
||||||
|
func getContents() -> Any?
|
||||||
|
func setContents(_ contents: Any?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol SparseItemGridView: UIView {
|
public protocol SparseItemGridView: UIView {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
import AVFoundation
|
||||||
import Display
|
import Display
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
@ -770,7 +771,23 @@ private final class DurationLayer: CALayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ItemLayer: CALayer, SparseItemGridLayer {
|
private protocol ItemLayer: SparseItemGridLayer {
|
||||||
|
var item: VisualMediaItem? { get set }
|
||||||
|
var durationLayer: DurationLayer? { get set }
|
||||||
|
var minFactor: CGFloat { get set }
|
||||||
|
var selectionLayer: GridMessageSelectionLayer? { get set }
|
||||||
|
var disposable: Disposable? { get set }
|
||||||
|
|
||||||
|
var hasContents: Bool { get set }
|
||||||
|
|
||||||
|
func updateDuration(duration: Int32?, isMin: Bool, minFactor: CGFloat)
|
||||||
|
func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool)
|
||||||
|
|
||||||
|
func bind(item: VisualMediaItem)
|
||||||
|
func unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class GenericItemLayer: CALayer, ItemLayer {
|
||||||
var item: VisualMediaItem?
|
var item: VisualMediaItem?
|
||||||
var durationLayer: DurationLayer?
|
var durationLayer: DurationLayer?
|
||||||
var minFactor: CGFloat = 1.0
|
var minFactor: CGFloat = 1.0
|
||||||
@ -792,6 +809,16 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
|||||||
deinit {
|
deinit {
|
||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getContents() -> Any? {
|
||||||
|
return self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
func setContents(_ contents: Any?) {
|
||||||
|
if let image = contents as? UIImage {
|
||||||
|
self.contents = image.cgImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func action(forKey event: String) -> CAAction? {
|
override func action(forKey event: String) -> CAAction? {
|
||||||
return nullAction
|
return nullAction
|
||||||
@ -865,6 +892,117 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemLayer {
|
||||||
|
var item: VisualMediaItem?
|
||||||
|
var durationLayer: DurationLayer?
|
||||||
|
var minFactor: CGFloat = 1.0
|
||||||
|
var selectionLayer: GridMessageSelectionLayer?
|
||||||
|
var disposable: Disposable?
|
||||||
|
|
||||||
|
var hasContents: Bool = false
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.contentsGravity = .resize
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func action(forKey event: String) -> CAAction? {
|
||||||
|
return nullAction
|
||||||
|
}
|
||||||
|
|
||||||
|
private var layerContents: Any?
|
||||||
|
func getContents() -> Any? {
|
||||||
|
return self.layerContents
|
||||||
|
}
|
||||||
|
|
||||||
|
func setContents(_ contents: Any?) {
|
||||||
|
self.layerContents = contents
|
||||||
|
|
||||||
|
if let image = contents as? UIImage {
|
||||||
|
self.layerContents = image.cgImage
|
||||||
|
if let cmSampleBuffer = image.cmSampleBuffer {
|
||||||
|
self.enqueue(cmSampleBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bind(item: VisualMediaItem) {
|
||||||
|
self.item = item
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDuration(duration: Int32?, isMin: Bool, minFactor: CGFloat) {
|
||||||
|
self.minFactor = minFactor
|
||||||
|
|
||||||
|
if let duration = duration {
|
||||||
|
if let durationLayer = self.durationLayer {
|
||||||
|
durationLayer.update(duration: duration, isMin: isMin)
|
||||||
|
} else {
|
||||||
|
let durationLayer = DurationLayer()
|
||||||
|
durationLayer.update(duration: duration, isMin: isMin)
|
||||||
|
self.addSublayer(durationLayer)
|
||||||
|
durationLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 3.0, y: self.bounds.height - 3.0), size: CGSize())
|
||||||
|
durationLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0)
|
||||||
|
self.durationLayer = durationLayer
|
||||||
|
}
|
||||||
|
} else if let durationLayer = self.durationLayer {
|
||||||
|
self.durationLayer = nil
|
||||||
|
durationLayer.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool) {
|
||||||
|
if let isSelected = isSelected {
|
||||||
|
if let selectionLayer = self.selectionLayer {
|
||||||
|
selectionLayer.updateSelected(isSelected, animated: animated)
|
||||||
|
} else {
|
||||||
|
let selectionLayer = GridMessageSelectionLayer(theme: theme)
|
||||||
|
selectionLayer.updateSelected(isSelected, animated: false)
|
||||||
|
self.selectionLayer = selectionLayer
|
||||||
|
self.addSublayer(selectionLayer)
|
||||||
|
if !self.bounds.isEmpty {
|
||||||
|
selectionLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
|
||||||
|
selectionLayer.updateLayout(size: self.bounds.size)
|
||||||
|
if animated {
|
||||||
|
selectionLayer.animateIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let selectionLayer = self.selectionLayer {
|
||||||
|
self.selectionLayer = nil
|
||||||
|
if animated {
|
||||||
|
selectionLayer.animateOut { [weak selectionLayer] in
|
||||||
|
selectionLayer?.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectionLayer.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unbind() {
|
||||||
|
self.item = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func needsShimmer() -> Bool {
|
||||||
|
return !self.hasContents
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize) {
|
||||||
|
/*if let durationLayer = self.durationLayer {
|
||||||
|
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class ItemView: UIView, SparseItemGridView {
|
private final class ItemView: UIView, SparseItemGridView {
|
||||||
var item: VisualMediaItem?
|
var item: VisualMediaItem?
|
||||||
var disposable: Disposable?
|
var disposable: Disposable?
|
||||||
@ -1037,6 +1175,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let chatLocation: ChatLocation
|
let chatLocation: ChatLocation
|
||||||
let directMediaImageCache: DirectMediaImageCache
|
let directMediaImageCache: DirectMediaImageCache
|
||||||
|
let captureProtected: Bool
|
||||||
var strings: PresentationStrings
|
var strings: PresentationStrings
|
||||||
let useListItems: Bool
|
let useListItems: Bool
|
||||||
let listItemInteraction: ListMessageItemInteraction
|
let listItemInteraction: ListMessageItemInteraction
|
||||||
@ -1055,13 +1194,14 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
|
|
||||||
private var shimmerImages: [CGFloat: UIImage] = [:]
|
private var shimmerImages: [CGFloat: UIImage] = [:]
|
||||||
|
|
||||||
init(context: AccountContext, chatLocation: ChatLocation, useListItems: Bool, listItemInteraction: ListMessageItemInteraction, chatControllerInteraction: ChatControllerInteraction, directMediaImageCache: DirectMediaImageCache) {
|
init(context: AccountContext, chatLocation: ChatLocation, useListItems: Bool, listItemInteraction: ListMessageItemInteraction, chatControllerInteraction: ChatControllerInteraction, directMediaImageCache: DirectMediaImageCache, captureProtected: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.chatLocation = chatLocation
|
self.chatLocation = chatLocation
|
||||||
self.useListItems = useListItems
|
self.useListItems = useListItems
|
||||||
self.listItemInteraction = listItemInteraction
|
self.listItemInteraction = listItemInteraction
|
||||||
self.chatControllerInteraction = chatControllerInteraction
|
self.chatControllerInteraction = chatControllerInteraction
|
||||||
self.directMediaImageCache = directMediaImageCache
|
self.directMediaImageCache = directMediaImageCache
|
||||||
|
self.captureProtected = captureProtected
|
||||||
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.strings = presentationData.strings
|
self.strings = presentationData.strings
|
||||||
@ -1174,7 +1314,11 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
if self.useListItems {
|
if self.useListItems {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ItemLayer()
|
if self.captureProtected {
|
||||||
|
return CaptureProtectedItemLayer()
|
||||||
|
} else {
|
||||||
|
return GenericItemLayer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createView() -> SparseItemGridView? {
|
func createView() -> SparseItemGridView? {
|
||||||
@ -1256,7 +1400,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
if let selectedMedia = selectedMedia {
|
if let selectedMedia = selectedMedia {
|
||||||
if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, possibleWidths: SparseItemGridBindingImpl.widthSpecs.1, synchronous: synchronous == .full) {
|
if let result = directMediaImageCache.getImage(message: message, media: selectedMedia, width: imageWidthSpec, possibleWidths: SparseItemGridBindingImpl.widthSpecs.1, synchronous: synchronous == .full) {
|
||||||
if let image = result.image {
|
if let image = result.image {
|
||||||
layer.contents = image.cgImage
|
layer.setContents(image)
|
||||||
switch synchronous {
|
switch synchronous {
|
||||||
case .none:
|
case .none:
|
||||||
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self, weak layer, weak displayItem] _ in
|
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self, weak layer, weak displayItem] _ in
|
||||||
@ -1286,9 +1430,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
synchronousValue = deltaTime < 0.1
|
synchronousValue = deltaTime < 0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
if layer.contents != nil && !synchronousValue {
|
if let contents = layer.getContents(), !synchronousValue {
|
||||||
let copyLayer = ItemLayer()
|
let copyLayer = GenericItemLayer()
|
||||||
copyLayer.contents = layer.contents
|
copyLayer.contents = contents
|
||||||
copyLayer.contentsRect = layer.contentsRect
|
copyLayer.contentsRect = layer.contentsRect
|
||||||
copyLayer.frame = layer.bounds
|
copyLayer.frame = layer.bounds
|
||||||
if let durationLayer = layer.durationLayer {
|
if let durationLayer = layer.durationLayer {
|
||||||
@ -1300,14 +1444,13 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
copyLayer?.removeFromSuperlayer()
|
copyLayer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
|
|
||||||
layer.contents = image?.cgImage
|
layer.setContents(image)
|
||||||
|
|
||||||
layer.hasContents = true
|
layer.hasContents = true
|
||||||
if let displayItem = displayItem {
|
if let displayItem = displayItem {
|
||||||
self?.updateShimmerLayersImpl?(displayItem)
|
self?.updateShimmerLayersImpl?(displayItem)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
layer.contents = image?.cgImage
|
layer.setContents(image)
|
||||||
|
|
||||||
if !synchronousValue {
|
if !synchronousValue {
|
||||||
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak layer] _ in
|
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak layer] _ in
|
||||||
@ -1510,7 +1653,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType, captureProtected: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.chatControllerInteraction = chatControllerInteraction
|
self.chatControllerInteraction = chatControllerInteraction
|
||||||
@ -1562,7 +1705,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
useListItems: useListItems,
|
useListItems: useListItems,
|
||||||
listItemInteraction: listItemInteraction,
|
listItemInteraction: listItemInteraction,
|
||||||
chatControllerInteraction: chatControllerInteraction,
|
chatControllerInteraction: chatControllerInteraction,
|
||||||
directMediaImageCache: self.directMediaImageCache
|
directMediaImageCache: self.directMediaImageCache,
|
||||||
|
captureProtected: captureProtected
|
||||||
)
|
)
|
||||||
|
|
||||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||||
@ -1787,20 +1931,6 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
let rect = strongSelf.itemGrid.frameForItem(layer: itemLayer)
|
let rect = strongSelf.itemGrid.frameForItem(layer: itemLayer)
|
||||||
|
|
||||||
/*let proxyNode = ASDisplayNode()
|
|
||||||
proxyNode.frame = rect
|
|
||||||
proxyNode.contents = itemLayer.contents
|
|
||||||
proxyNode.isHidden = true
|
|
||||||
strongSelf.addSubnode(proxyNode)
|
|
||||||
|
|
||||||
let escapeNotification = EscapeNotification {
|
|
||||||
proxyNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
|
|
||||||
Queue.mainQueue().after(1.0, {
|
|
||||||
escapeNotification.keep()
|
|
||||||
})*/
|
|
||||||
|
|
||||||
strongSelf.chatControllerInteraction.openMessageContextActions(message, strongSelf, rect, gesture)
|
strongSelf.chatControllerInteraction.openMessageContextActions(message, strongSelf, rect, gesture)
|
||||||
|
|
||||||
strongSelf.itemGrid.cancelGestures()
|
strongSelf.itemGrid.cancelGestures()
|
||||||
|
@ -392,10 +392,11 @@ private final class PeerInfoPendingPane {
|
|||||||
openMediaCalendar: @escaping () -> Void,
|
openMediaCalendar: @escaping () -> Void,
|
||||||
paneDidScroll: @escaping () -> Void
|
paneDidScroll: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
|
let captureProtected = data.peer?.isCopyProtectionEnabled ?? false
|
||||||
let paneNode: PeerInfoPaneNode
|
let paneNode: PeerInfoPaneNode
|
||||||
switch key {
|
switch key {
|
||||||
case .media:
|
case .media:
|
||||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .photoOrVideo)
|
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .photoOrVideo, captureProtected: captureProtected)
|
||||||
paneNode = visualPaneNode
|
paneNode = visualPaneNode
|
||||||
visualPaneNode.openCurrentDate = {
|
visualPaneNode.openCurrentDate = {
|
||||||
openMediaCalendar()
|
openMediaCalendar()
|
||||||
@ -404,17 +405,17 @@ private final class PeerInfoPendingPane {
|
|||||||
paneDidScroll()
|
paneDidScroll()
|
||||||
}
|
}
|
||||||
case .files:
|
case .files:
|
||||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .files)
|
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .files, captureProtected: captureProtected)
|
||||||
paneNode = visualPaneNode
|
paneNode = visualPaneNode
|
||||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .file)
|
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .file)
|
||||||
case .links:
|
case .links:
|
||||||
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .webPage)
|
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .webPage)
|
||||||
case .voice:
|
case .voice:
|
||||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .voiceAndVideoMessages)
|
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .voiceAndVideoMessages, captureProtected: captureProtected)
|
||||||
paneNode = visualPaneNode
|
paneNode = visualPaneNode
|
||||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .voiceOrInstantVideo)
|
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .voiceOrInstantVideo)
|
||||||
case .music:
|
case .music:
|
||||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .music)
|
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .music, captureProtected: captureProtected)
|
||||||
paneNode = visualPaneNode
|
paneNode = visualPaneNode
|
||||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .music)
|
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .music)
|
||||||
case .gifs:
|
case .gifs:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user