Video avatar fixes

This commit is contained in:
Ilya Laktyushin 2020-06-27 04:12:09 +03:00
parent fd897989eb
commit 05141a7ec4
4 changed files with 64 additions and 29 deletions

View File

@ -113,8 +113,8 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
var index: Int32 = 0 var index: Int32 = 0
for photo in photos { for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count)) let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty { if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData)) result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else { } else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData)) result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} }
@ -140,8 +140,8 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry
var index: Int32 = 0 var index: Int32 = 0
for photo in photos { for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count)) let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty { if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData)) result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else { } else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData)) result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} }

View File

@ -1415,7 +1415,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
if let profileImage = peer?.smallProfileImage { if let profileImage = peer?.smallProfileImage {
state.updatingAvatar = .image(profileImage, false) state.updatingAvatar = .image(profileImage, false)
} else { } else {
state.updatingAvatar = .none state.updatingAvatar = ItemListAvatarAndNameInfoItemUpdatingAvatar.none
} }
return state return state
} }

View File

@ -236,7 +236,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let id = id { if let video = videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in videoNode.ownsContentNodeUpdated = { [weak self] owns in
@ -626,7 +626,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
strongSelf.items = items strongSelf.items = items
strongSelf.itemsUpdated?(items) strongSelf.itemsUpdated?(items)
if let size = strongSelf.validLayout { if let size = strongSelf.validLayout {
strongSelf.updateItems(size: size, update: true, transition: .immediate, stripTransition: .immediate, synchronous: true) strongSelf.updateItems(size: size, update: true, transition: .immediate, stripTransition: .immediate, synchronous: synchronous)
} }
if items.isEmpty { if items.isEmpty {
if !strongSelf.didSetReady { if !strongSelf.didSetReady {
@ -762,7 +762,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let context: AccountContext let context: AccountContext
let avatarNode: AvatarNode let avatarNode: AvatarNode
private var videoNode: UniversalVideoNode? fileprivate var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent? private var videoContent: NativeVideoContent?
var tapped: (() -> Void)? var tapped: (() -> Void)?
@ -788,6 +788,21 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
} }
} }
func reattachVideoNode() {
if let videoNode = self.videoNode {
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
videoNode.transform = CATransform3DIdentity
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
videoNode.frame = self.avatarNode.frame
self.addSubnode(videoNode)
}
}
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) { func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
if let peer = peer { if let peer = peer {
var overrideImage: AvatarNodeImageOverride? var overrideImage: AvatarNodeImageOverride?
@ -803,7 +818,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference { if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
let id = imageId let id = imageId
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id { if videoContent.id != self.videoContent?.id {
let mediaManager = self.context.sharedContext.mediaManager let mediaManager = self.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
@ -835,9 +850,19 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
videoNode.frame = self.avatarNode.frame videoNode.frame = self.avatarNode.frame
if isExpanded == videoNode.canAttachContent { if isExpanded == videoNode.canAttachContent {
videoNode.canAttachContent = !isExpanded let update = {
if videoNode.canAttachContent { videoNode.canAttachContent = !isExpanded
videoNode.play() if videoNode.canAttachContent {
videoNode.seek(0.0)
videoNode.play()
}
}
if isExpanded {
DispatchQueue.main.async {
update()
}
} else {
update()
} }
} }
} }
@ -1011,20 +1036,28 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
} }
func animateAvatarCollapse(transition: ContainedViewLayoutTransition) { func animateAvatarCollapse(transition: ContainedViewLayoutTransition) {
if let currentItemNode = self.listContainerNode.currentItemNode, let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage, case .animated = transition { if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition {
let avatarCopyView = UIImageView() if let videoNode = self.avatarContainerNode.videoNode {
avatarCopyView.image = unroundedImage // videoNode.position = currentItemNode.imageNode.position
avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame // currentItemNode.addSubnode(videoNode)
avatarCopyView.center = currentItemNode.imageNode.position // let scale = currentItemNode.imageNode.bounds.height / videoNode.bounds.height
currentItemNode.view.addSubview(avatarCopyView) // avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale)
let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height // self.avatarContainerNode.reattachVideoNode()
avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale) } else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage {
avatarCopyView.alpha = 0.0 let avatarCopyView = UIImageView()
transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in avatarCopyView.image = unroundedImage
Queue.mainQueue().after(0.1, { avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame
avatarCopyView?.removeFromSuperview() avatarCopyView.center = currentItemNode.imageNode.position
currentItemNode.view.addSubview(avatarCopyView)
let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height
avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale)
avatarCopyView.alpha = 0.0
transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in
Queue.mainQueue().after(0.1, {
avatarCopyView?.removeFromSuperview()
})
}) })
}) }
} }
} }
} }

View File

@ -31,11 +31,12 @@ public final class NativeVideoContent: UniversalVideoContent {
public let baseRate: Double public let baseRate: Double
let fetchAutomatically: Bool let fetchAutomatically: Bool
let onlyFullSizeThumbnail: Bool let onlyFullSizeThumbnail: Bool
let autoFetchFullSizeThumbnail: Bool
let continuePlayingWithoutSoundOnLostAudioSession: Bool let continuePlayingWithoutSoundOnLostAudioSession: Bool
let placeholderColor: UIColor let placeholderColor: UIColor
let tempFilePath: String? let tempFilePath: String?
public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) { public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
self.id = id self.id = id
self.nativeId = id self.nativeId = id
self.fileReference = fileReference self.fileReference = fileReference
@ -60,13 +61,14 @@ public final class NativeVideoContent: UniversalVideoContent {
self.baseRate = baseRate self.baseRate = baseRate
self.fetchAutomatically = fetchAutomatically self.fetchAutomatically = fetchAutomatically
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
self.placeholderColor = placeholderColor self.placeholderColor = placeholderColor
self.tempFilePath = tempFilePath self.tempFilePath = tempFilePath
} }
public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode { public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath) return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
} }
public func isEqual(to other: UniversalVideoContent) -> Bool { public func isEqual(to other: UniversalVideoContent) -> Bool {
@ -126,7 +128,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
private var shouldPlay: Bool = false private var shouldPlay: Bool = false
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) { init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
self.postbox = postbox self.postbox = postbox
self.fileReference = fileReference self.fileReference = fileReference
self.placeholderColor = placeholderColor self.placeholderColor = placeholderColor
@ -163,7 +165,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
self?.performActionAtEnd() self?.performActionAtEnd()
} }
self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail || fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
Queue.mainQueue().async { Queue.mainQueue().async {
if let strongSelf = self, strongSelf.dimensions == nil { if let strongSelf = self, strongSelf.dimensions == nil {
if let dimensions = getSize() { if let dimensions = getSize() {