mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Various improvements
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user