Various improvements

This commit is contained in:
Ilya Laktyushin
2022-07-08 14:30:49 +02:00
parent 0c2f2ec1b7
commit 9d41d0f110
13 changed files with 510 additions and 123 deletions

View File

@@ -147,10 +147,15 @@ private let moreButtonImage = generateTintedImage(image: UIImage(bundleImageName
private let placeholderFont = Font.regular(16.0)
private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode {
enum Mode {
case pictureInPicture
case airplay
}
private let iconNode: ASImageNode
private let textNode: ASTextNode
init(strings: PresentationStrings) {
init(strings: PresentationStrings, mode: Mode) {
self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true
self.iconNode.displayWithoutProcessing = true
@@ -160,10 +165,20 @@ private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode
self.textNode = ASTextNode()
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = false
self.textNode.attributedText = NSAttributedString(string: strings.Embed_PlayingInPIP, font: placeholderFont, textColor: UIColor(rgb: 0x8e8e93))
let text: String
switch mode {
case .pictureInPicture:
text = strings.Embed_PlayingInPIP
case .airplay:
text = strings.Gallery_AirPlayPlaceholder
}
self.textNode.attributedText = NSAttributedString(string: text, font: placeholderFont, textColor: UIColor(rgb: 0x8e8e93))
super.init()
self.backgroundColor = UIColor(rgb: 0x333335)
self.addSubnode(self.iconNode)
self.addSubnode(self.textNode)
}
@@ -975,7 +990,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if let pictureInPictureNode = self.pictureInPictureNode {
if let item = self.item {
let placeholderSize = item.content.dimensions.fitted(layout.size)
var placeholderSize = item.content.dimensions.fitted(layout.size)
placeholderSize.height += 2.0
transition.updateFrame(node: pictureInPictureNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - placeholderSize.width) / 2.0), y: floor((layout.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
pictureInPictureNode.updateLayout(placeholderSize, transition: transition)
}
@@ -1144,11 +1160,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.videoNode = videoNode
self.videoNodeUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
videoNode.isUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335)
videoNode.backgroundColor = UIColor.black
if item.fromPlayingVideo {
videoNode.canAttachContent = false
} else {
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
self.updateDisplayPlaceholder()
}
scrubberView.setStatusSignal(videoNode.status |> map { value -> MediaPlayerStatus in
@@ -1492,21 +1508,25 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.videoNode?.notifyPlaybackControlsHidden(!isVisible)
}
private func updateDisplayPlaceholder() {
self.updateDisplayPlaceholder(!(self.videoNode?.ownsContentNode ?? true) || self.isAirPlayActive)
}
private func updateDisplayPlaceholder(_ displayPlaceholder: Bool) {
if displayPlaceholder && !self.disablePictureInPicturePlaceholder {
if self.pictureInPictureNode == nil {
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings)
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings, mode: self.isAirPlayActive ? .airplay : .pictureInPicture)
pictureInPictureNode.isUserInteractionEnabled = false
self.pictureInPictureNode = pictureInPictureNode
self.insertSubnode(pictureInPictureNode, aboveSubnode: self.scrollNode)
if let validLayout = self.validLayout {
if let item = self.item {
let placeholderSize = item.content.dimensions.fitted(validLayout.0.size)
pictureInPictureNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.0.size.width - placeholderSize.width) / 2.0), y: floor((validLayout.0.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)
var placeholderSize = item.content.dimensions.fitted(validLayout.0.size)
placeholderSize.height += 2.0
pictureInPictureNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.0.size.width - placeholderSize.width) / 2.0), y: floorToScreenPixels((validLayout.0.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)
pictureInPictureNode.updateLayout(placeholderSize, transition: .immediate)
}
}
self.videoNode?.backgroundColor = UIColor(rgb: 0x333335)
}
} else if let pictureInPictureNode = self.pictureInPictureNode {
self.pictureInPictureNode = nil
@@ -1602,10 +1622,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
} else {
videoNode.continuePlayingWithoutSound()
}
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
self.updateDisplayPlaceholder()
} else if !item.fromPlayingVideo {
videoNode.canAttachContent = isVisible
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
self.updateDisplayPlaceholder()
}
if self.shouldAutoplayOnCentrality() {
self.hideStatusNodeUntilCentrality = true
@@ -1690,7 +1710,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
videoNode.canAttachContent = true
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
self.updateDisplayPlaceholder()
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
} else {
@@ -1768,7 +1788,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if self.item?.fromPlayingVideo ?? false {
Queue.mainQueue().after(0.001) {
videoNode.canAttachContent = true
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
self.updateDisplayPlaceholder()
}
}
@@ -2457,6 +2477,16 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
})))
if #available(iOS 11.0, *) {
items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
f(.default)
guard let strongSelf = self else {
return
}
strongSelf.beginAirPlaySetup()
})))
}
if let (message, _, _) = strongSelf.contentInfo() {
for media in message.media {
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
@@ -2578,63 +2608,85 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
return items
}
}
private var isAirPlayActive = false
private var externalVideoPlayer: ExternalVideoPlayer?
func beginAirPlaySetup() {
guard let content = self.item?.content as? NativeVideoContent else {
return
}
if #available(iOS 11.0, *) {
self.externalVideoPlayer = ExternalVideoPlayer(context: self.context, content: content)
self.externalVideoPlayer?.openRouteSelection()
self.externalVideoPlayer?.isActiveUpdated = { [weak self] isActive in
if let strongSelf = self {
if strongSelf.isAirPlayActive && !isActive {
strongSelf.externalVideoPlayer = nil
}
strongSelf.isAirPlayActive = isActive
strongSelf.updateDisplayPlaceholder()
}
}
}
}
@objc func openStickersButtonPressed() {
if let content = self.item?.content as? NativeVideoContent {
let context = self.context
let media = content.fileReference.abstract
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let topController = (self.baseNavigationController()?.topViewController as? ViewController)
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
topController?.present(controller, in: .window(.root), with: nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
self.isInteractingPromise.set(true)
let signal = self.context.engine.stickers.stickerPacksAttachedToMedia(media: media)
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak self] packs in
guard let strongSelf = self, !packs.isEmpty else {
return
}
let baseNavigationController = strongSelf.baseNavigationController()
baseNavigationController?.view.endEditing(true)
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in
let animateInAsReplacement = false
switch action {
case .add:
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}), in: .window(.root))
case let .remove(positionInList):
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
}
return true
}), in: .window(.root))
}
}, dismissed: { [weak self] in
self?.isInteractingPromise.set(false)
})
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
})
guard let content = self.item?.content as? NativeVideoContent else {
return
}
let context = self.context
let media = content.fileReference.abstract
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let topController = (self.baseNavigationController()?.topViewController as? ViewController)
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
topController?.present(controller, in: .window(.root), with: nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
self.isInteractingPromise.set(true)
let signal = self.context.engine.stickers.stickerPacksAttachedToMedia(media: media)
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak self] packs in
guard let strongSelf = self, !packs.isEmpty else {
return
}
let baseNavigationController = strongSelf.baseNavigationController()
baseNavigationController?.view.endEditing(true)
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in
let animateInAsReplacement = false
switch action {
case .add:
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}), in: .window(.root))
case let .remove(positionInList):
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
}
return true
}), in: .window(.root))
}
}, dismissed: { [weak self] in
self?.isInteractingPromise.set(false)
})
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
})
}
override func adjustForPreviewing() {