mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
0c2f2ec1b7
commit
9d41d0f110
BIN
Telegram/Telegram-iOS/Resources/Gift.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Gift.tgs
Normal file
Binary file not shown.
@ -7831,6 +7831,9 @@ Sorry for the inconvenience.";
|
||||
"Notification.PremiumGift.Sent" = "%1$@ sent you a gift for %2$@";
|
||||
"Notification.PremiumGift.SentYou" = "You sent a gift for %@";
|
||||
|
||||
"Notification.PremiumGift.Months_1" = "%@ month";
|
||||
"Notification.PremiumGift.Months_any" = "%@ months";
|
||||
|
||||
"Notification.PremiumGift.Title" = "Telegram Premium";
|
||||
"Notification.PremiumGift.Subtitle" = "for %@";
|
||||
"Notification.PremiumGift.View" = "View";
|
||||
|
@ -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() {
|
||||
|
@ -799,11 +799,11 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
let duration: Int32
|
||||
switch product.id {
|
||||
case "org.telegram.telegramPremium.twelveMonths":
|
||||
duration = 86400 * 365
|
||||
duration = 12
|
||||
case "org.telegram.telegramPremium.sixMonths":
|
||||
duration = 86400 * 180
|
||||
duration = 6
|
||||
case "org.telegram.telegramPremium.threeMonths":
|
||||
duration = 86400 * 90
|
||||
duration = 3
|
||||
default:
|
||||
duration = 0
|
||||
}
|
||||
@ -1259,13 +1259,16 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
completionImpl = { [weak self] duration in
|
||||
if let strongSelf = self {
|
||||
let navigationController = strongSelf.navigationController
|
||||
strongSelf.dismiss()
|
||||
let introController = PremiumIntroScreen(context: context, source: .gift(from: context.account.peerId, to: peerId, duration: duration))
|
||||
navigationController?.pushViewController(introController, animated: true)
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
// let introController = PremiumIntroScreen(context: context, source: .gift(from: context.account.peerId, to: peerId, duration: duration))
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is PeerInfoScreen) && !($0 is PremiumGiftScreen) }
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|
||||
Queue.mainQueue().after(0.1, {
|
||||
introController.view.addSubview(ConfettiView(frame: introController.view.bounds))
|
||||
if let topController = navigationController.viewControllers.first {
|
||||
topController.view.addSubview(ConfettiView(frame: topController.view.bounds))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1487,21 +1487,21 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
||||
} else if case let .gift(fromPeerId, _, duration) = context.component.source {
|
||||
if fromPeerId == context.component.context.account.peerId {
|
||||
if duration >= 86400 * 365 {
|
||||
if duration == 12 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
||||
} else if duration >= 86400 * 180 {
|
||||
} else if duration == 6 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_6Month(otherPeerName).string
|
||||
} else if duration >= 86400 * 90 {
|
||||
} else if duration == 3 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_3Month(otherPeerName).string
|
||||
} else {
|
||||
secondaryTitleText = ""
|
||||
}
|
||||
} else {
|
||||
if duration >= 86400 * 365 {
|
||||
if duration == 12 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_12Month(otherPeerName).string
|
||||
} else if duration >= 86400 * 180 {
|
||||
} else if duration == 6 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_6Month(otherPeerName).string
|
||||
} else if duration >= 86400 * 90 {
|
||||
} else if duration == 3 {
|
||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_3Month(otherPeerName).string
|
||||
} else {
|
||||
secondaryTitleText = ""
|
||||
|
@ -1190,8 +1190,21 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
||||
view.expandFromPictureInPicture()
|
||||
}
|
||||
|
||||
if let validLayout = self.validLayout {
|
||||
self.view.clipsToBounds = true
|
||||
self.view.layer.cornerRadius = validLayout.deviceMetrics.screenCornerRadius
|
||||
if #available(iOS 13.0, *) {
|
||||
self.view.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.view.layer.animatePosition(from: CGPoint(x: 353.0, y: 117.0), to: self.view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||
self?.view.layer.cornerRadius = 0.0
|
||||
})
|
||||
self.view.layer.animateScale(from: 0.001, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
||||
self.view.layer.allowsGroupOpacity = true
|
||||
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] _ in
|
||||
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1226,6 +1239,18 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
||||
strongSelf.view.layer.allowsGroupOpacity = false
|
||||
strongSelf.dismissImpl(completion: completion)
|
||||
})
|
||||
|
||||
if let validLayout = self.validLayout {
|
||||
self.view.clipsToBounds = true
|
||||
self.view.layer.cornerRadius = validLayout.deviceMetrics.screenCornerRadius
|
||||
if #available(iOS 13.0, *) {
|
||||
self.view.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.view.layer.animatePosition(from: self.view.center, to: CGPoint(x: 353.0, y: 117.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
})
|
||||
self.view.layer.animateScale(from: 1.0, to: 0.001, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
|
||||
private func dismissImpl(completion: (() -> Void)? = nil) {
|
||||
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "gift.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
188
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Airplay.pdf
vendored
Normal file
188
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Airplay.pdf
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.335022 2.334961 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
3.865000 17.330078 m
|
||||
3.837526 17.330078 l
|
||||
3.300828 17.330086 2.857980 17.330093 2.497252 17.300621 c
|
||||
2.122623 17.270012 1.778399 17.204330 1.455116 17.039610 c
|
||||
0.953664 16.784107 0.545970 16.376415 0.290468 15.874963 c
|
||||
0.125747 15.551680 0.060066 15.207455 0.029457 14.832827 c
|
||||
-0.000016 14.472098 -0.000009 14.029249 0.000000 13.492552 c
|
||||
0.000000 13.465077 l
|
||||
0.000000 7.865078 l
|
||||
0.000000 7.837605 l
|
||||
-0.000009 7.300907 -0.000016 6.858058 0.029457 6.497330 c
|
||||
0.060066 6.122702 0.125747 5.778477 0.290468 5.455194 c
|
||||
0.545970 4.953741 0.953664 4.546048 1.455116 4.290545 c
|
||||
1.778399 4.125825 2.122623 4.060143 2.497252 4.029535 c
|
||||
2.857977 4.000063 3.300821 4.000070 3.837511 4.000078 c
|
||||
3.837541 4.000078 l
|
||||
3.865000 4.000078 l
|
||||
4.665000 4.000078 l
|
||||
5.032270 4.000078 5.330000 4.297809 5.330000 4.665078 c
|
||||
5.330000 5.032348 5.032270 5.330078 4.665000 5.330078 c
|
||||
3.865000 5.330078 l
|
||||
3.293975 5.330078 2.905699 5.330595 2.605556 5.355118 c
|
||||
2.313176 5.379006 2.163463 5.422318 2.058923 5.475584 c
|
||||
1.807727 5.603576 1.603498 5.807804 1.475507 6.059001 c
|
||||
1.422241 6.163542 1.378929 6.313254 1.355040 6.605635 c
|
||||
1.330518 6.905777 1.330000 7.294052 1.330000 7.865078 c
|
||||
1.330000 13.465077 l
|
||||
1.330000 14.036103 1.330518 14.424378 1.355040 14.724522 c
|
||||
1.378929 15.016902 1.422241 15.166615 1.475507 15.271155 c
|
||||
1.603498 15.522351 1.807727 15.726581 2.058923 15.854571 c
|
||||
2.163463 15.907838 2.313176 15.951150 2.605556 15.975038 c
|
||||
2.905699 15.999560 3.293974 16.000078 3.865000 16.000078 c
|
||||
15.465000 16.000078 l
|
||||
16.036026 16.000078 16.424301 15.999560 16.724445 15.975038 c
|
||||
17.016823 15.951150 17.166538 15.907838 17.271076 15.854571 c
|
||||
17.522274 15.726581 17.726501 15.522351 17.854492 15.271155 c
|
||||
17.907761 15.166615 17.951073 15.016902 17.974960 14.724522 c
|
||||
17.999481 14.424378 18.000000 14.036104 18.000000 13.465078 c
|
||||
18.000000 7.865078 l
|
||||
18.000000 7.294053 17.999481 6.905778 17.974960 6.605635 c
|
||||
17.951073 6.313254 17.907761 6.163542 17.854492 6.059001 c
|
||||
17.726501 5.807804 17.522274 5.603576 17.271076 5.475584 c
|
||||
17.166538 5.422318 17.016823 5.379006 16.724445 5.355118 c
|
||||
16.424301 5.330595 16.036026 5.330078 15.465000 5.330078 c
|
||||
14.665000 5.330078 l
|
||||
14.297730 5.330078 14.000000 5.032348 14.000000 4.665078 c
|
||||
14.000000 4.297809 14.297730 4.000078 14.665000 4.000078 c
|
||||
15.465000 4.000078 l
|
||||
15.492459 4.000078 l
|
||||
15.492488 4.000078 l
|
||||
16.029179 4.000070 16.472023 4.000063 16.832748 4.029535 c
|
||||
17.207378 4.060143 17.551601 4.125825 17.874886 4.290545 c
|
||||
18.376335 4.546048 18.784031 4.953741 19.039532 5.455194 c
|
||||
19.204254 5.778477 19.269936 6.122702 19.300545 6.497330 c
|
||||
19.330015 6.858045 19.330009 7.300875 19.330002 7.837547 c
|
||||
19.330002 7.837619 l
|
||||
19.330002 7.865078 l
|
||||
19.330002 13.465078 l
|
||||
19.330002 13.492537 l
|
||||
19.330002 13.492610 l
|
||||
19.330009 14.029282 19.330015 14.472111 19.300545 14.832827 c
|
||||
19.269936 15.207455 19.204254 15.551680 19.039532 15.874963 c
|
||||
18.784031 16.376415 18.376335 16.784107 17.874886 17.039610 c
|
||||
17.551601 17.204330 17.207378 17.270012 16.832748 17.300621 c
|
||||
16.472019 17.330093 16.029171 17.330086 15.492474 17.330078 c
|
||||
15.465000 17.330078 l
|
||||
3.865000 17.330078 l
|
||||
h
|
||||
9.549997 5.417668 m
|
||||
9.624264 5.444814 9.705738 5.444814 9.780005 5.417668 c
|
||||
9.779352 5.417907 9.779119 5.418036 9.779340 5.417960 c
|
||||
9.779449 5.417922 9.779672 5.417834 9.780010 5.417685 c
|
||||
9.785703 5.415166 9.824282 5.395164 9.915651 5.303084 c
|
||||
10.027624 5.190243 10.164604 5.026791 10.383287 4.764371 c
|
||||
11.967221 2.863649 l
|
||||
12.324518 2.434894 12.562548 2.148232 12.716402 1.925475 c
|
||||
12.838650 1.748481 12.861221 1.674737 12.864748 1.663215 c
|
||||
12.864949 1.662560 l
|
||||
12.864219 1.564913 12.820898 1.472416 12.746351 1.409344 c
|
||||
12.745722 1.409081 l
|
||||
12.734627 1.404418 12.663531 1.374542 12.449274 1.355145 c
|
||||
12.179653 1.330734 11.807049 1.330078 11.248934 1.330078 c
|
||||
8.081068 1.330078 l
|
||||
7.522953 1.330078 7.150349 1.330734 6.880728 1.355145 c
|
||||
6.666473 1.374543 6.595378 1.404416 6.584280 1.409080 c
|
||||
6.583652 1.409343 l
|
||||
6.509105 1.472416 6.465783 1.564913 6.465053 1.662560 c
|
||||
6.465254 1.663213 l
|
||||
6.468780 1.674733 6.491349 1.748475 6.613600 1.925474 c
|
||||
6.767454 2.148230 7.005483 2.434893 7.362779 2.863647 c
|
||||
8.946714 4.764370 l
|
||||
9.165398 5.026790 9.302377 5.190243 9.414351 5.303084 c
|
||||
9.505718 5.395163 9.544298 5.415166 9.549992 5.417685 c
|
||||
9.551012 5.418137 9.550975 5.418026 9.549997 5.417668 c
|
||||
h
|
||||
10.236588 6.666842 m
|
||||
9.867472 6.801756 9.462530 6.801756 9.093414 6.666842 c
|
||||
8.834289 6.572128 8.636753 6.407670 8.470269 6.239893 c
|
||||
8.312561 6.080961 8.138463 5.872022 7.942029 5.636275 c
|
||||
7.924980 5.615815 l
|
||||
6.341045 3.715093 l
|
||||
6.320368 3.690281 l
|
||||
5.989172 3.292866 5.711553 2.959741 5.519254 2.681324 c
|
||||
5.330824 2.408508 5.141049 2.075929 5.135232 1.690181 c
|
||||
5.127517 1.178505 5.355523 0.691704 5.753542 0.370064 c
|
||||
6.053606 0.127583 6.430592 0.060459 6.760806 0.030563 c
|
||||
7.097775 0.000055 7.531381 0.000065 8.048665 0.000076 c
|
||||
8.048759 0.000076 l
|
||||
8.081068 0.000076 l
|
||||
11.248934 0.000076 l
|
||||
11.281242 0.000076 l
|
||||
11.281337 0.000076 l
|
||||
11.798622 0.000065 12.232226 0.000055 12.569197 0.030563 c
|
||||
12.899410 0.060459 13.276397 0.127583 13.576460 0.370064 c
|
||||
13.974480 0.691704 14.202486 1.178505 14.194770 1.690181 c
|
||||
14.188953 2.075929 13.999178 2.408508 13.810747 2.681325 c
|
||||
13.618445 2.959745 13.340822 3.292877 13.009618 3.690300 c
|
||||
12.988955 3.715096 l
|
||||
11.405022 5.615816 l
|
||||
11.387974 5.636274 l
|
||||
11.191540 5.872021 11.017441 6.080961 10.859733 6.239894 c
|
||||
10.693249 6.407670 10.495712 6.572128 10.236588 6.666842 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
5505
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000005595 00000 n
|
||||
0000005618 00000 n
|
||||
0000005791 00000 n
|
||||
0000005865 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
5924
|
||||
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Airplay.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ import UrlEscaping
|
||||
import TelegramStringFormatting
|
||||
import WallpaperBackgroundNode
|
||||
import ReactionSelectionNode
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: false)
|
||||
@ -29,8 +31,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private let mediaBackgroundNode: NavigationBackgroundNode
|
||||
private let titleNode: TextNode
|
||||
private let subtitleNode: TextNode
|
||||
|
||||
private let giftNode: ASImageNode
|
||||
private let animationNode: AnimatedStickerNode
|
||||
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let buttonStarsNode: PremiumStarsNode
|
||||
@ -62,10 +63,10 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.clipsToBounds = true
|
||||
self.buttonNode.cornerRadius = 17.0
|
||||
|
||||
self.giftNode = ASImageNode()
|
||||
self.giftNode.isUserInteractionEnabled = false
|
||||
self.giftNode.displaysAsynchronously = false
|
||||
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Gift"), width: 384, height: 384, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
self.buttonStarsNode = PremiumStarsNode()
|
||||
|
||||
@ -80,7 +81,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
self.addSubnode(self.mediaBackgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.giftNode)
|
||||
self.addSubnode(self.animationNode)
|
||||
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.buttonNode.addSubnode(self.buttonStarsNode)
|
||||
@ -128,7 +129,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
||||
|
||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||
let imageSize = CGSize(width: 220.0, height: 210.0)
|
||||
let giftSize = CGSize(width: 220.0, height: 240.0)
|
||||
|
||||
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: item.message, accountPeerId: item.context.account.peerId)
|
||||
|
||||
@ -139,7 +140,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .giftPremium(_, _, durationValue):
|
||||
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(timeIntervalString(strings: item.presentationData.strings, value: durationValue)).string
|
||||
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(durationValue)).string
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -148,11 +149,11 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_Title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_Title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: duration, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: duration, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var labelRects = labelLayout.linesRects()
|
||||
if labelRects.count > 1 {
|
||||
@ -184,7 +185,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
backgroundMaskUpdated = true
|
||||
}
|
||||
|
||||
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + imageSize.height + 18.0)
|
||||
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + giftSize.height + 18.0)
|
||||
|
||||
return (backgroundSize.width, { boundingWidth in
|
||||
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
||||
@ -193,7 +194,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: labelLayout.size.height + 16.0), size: imageSize)
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - giftSize.width) / 2.0), y: labelLayout.size.height + 16.0), size: giftSize)
|
||||
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
|
||||
|
||||
@ -201,10 +202,9 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.mediaBackgroundNode.update(size: mediaBackgroundFrame.size, transition: .immediate)
|
||||
strongSelf.buttonNode.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
|
||||
|
||||
strongSelf.giftNode.image = UIImage(bundleImageName: "Components/Gift")
|
||||
if let image = strongSelf.giftNode.image {
|
||||
strongSelf.giftNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - image.size.width) / 2.0), y: mediaBackgroundFrame.minY + 14.0), size: image.size)
|
||||
}
|
||||
let iconSize = CGSize(width: 160.0, height: 160.0)
|
||||
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY - 16.0), size: iconSize)
|
||||
strongSelf.animationNode.updateLayout(size: iconSize)
|
||||
|
||||
let _ = labelApply()
|
||||
let _ = titleApply()
|
||||
@ -214,7 +214,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: 2.0), size: labelLayout.size)
|
||||
strongSelf.labelNode.frame = labelFrame
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 121.0), size: titleLayout.size)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY - 1.0), size: subtitleLayout.size)
|
||||
|
@ -0,0 +1,121 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import SwiftSignalKit
|
||||
import UniversalMediaPlayer
|
||||
import AccountContext
|
||||
import AVKit
|
||||
|
||||
public class ExternalVideoPlayer: NSObject, AVRoutePickerViewDelegate {
|
||||
private let context: AccountContext
|
||||
let content: NativeVideoContent
|
||||
|
||||
let player: AVPlayer?
|
||||
private var didPlayToEndTimeObserver: NSObjectProtocol?
|
||||
private var timeObserver: Any?
|
||||
|
||||
private var statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: false, progress: 0.0, display: true), soundEnabled: true)
|
||||
private let _status = ValuePromise<MediaPlayerStatus>()
|
||||
var status: Signal<MediaPlayerStatus, NoError> {
|
||||
return self._status.get()
|
||||
}
|
||||
private var seekId: Int = 0
|
||||
|
||||
private weak var routePickerView: UIView?
|
||||
|
||||
public var isActiveUpdated: (Bool) -> Void = { _ in }
|
||||
|
||||
public init(context: AccountContext, content: NativeVideoContent) {
|
||||
self.context = context
|
||||
self.content = content
|
||||
|
||||
if let path = context.account.postbox.mediaBox.completedResourcePath(content.fileReference.media.resource, pathExtension: "mp4") {
|
||||
let player = AVPlayer(url: URL(fileURLWithPath: path))
|
||||
self.player = player
|
||||
} else {
|
||||
self.player = nil
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.startObservingForAirPlayStatusChanges()
|
||||
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||
|
||||
if let player = self.player {
|
||||
self.didPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil, using: { [weak self] notification in
|
||||
if let strongSelf = self {
|
||||
strongSelf.player?.seek(to: CMTime(seconds: 0.0, preferredTimescale: 30))
|
||||
strongSelf.play()
|
||||
}
|
||||
})
|
||||
|
||||
self.timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 10), queue: DispatchQueue.main) { [weak self] time in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: strongSelf.statusValue.duration, dimensions: CGSize(), timestamp: CMTimeGetSeconds(time), baseRate: 1.0, seekId: strongSelf.seekId, status: strongSelf.statusValue.status, soundEnabled: true)
|
||||
strongSelf._status.set(strongSelf.statusValue)
|
||||
}
|
||||
}
|
||||
|
||||
self._status.set(self.statusValue)
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let timeObserver = self.timeObserver {
|
||||
self.player?.removeTimeObserver(timeObserver)
|
||||
}
|
||||
|
||||
if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver {
|
||||
NotificationCenter.default.removeObserver(didPlayToEndTimeObserver)
|
||||
}
|
||||
|
||||
self.stopObservingForAirPlayStatusChanges()
|
||||
}
|
||||
|
||||
public func play() {
|
||||
self.player?.play()
|
||||
}
|
||||
|
||||
public func openRouteSelection() {
|
||||
if #available(iOS 11.0, *) {
|
||||
let routePickerView = AVRoutePickerView()
|
||||
routePickerView.delegate = self
|
||||
if #available(iOS 13.0, *) {
|
||||
routePickerView.prioritizesVideoDevices = true
|
||||
}
|
||||
self.context.sharedContext.mainWindow?.viewController?.view.addSubview(routePickerView)
|
||||
|
||||
if let routePickerButton = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
|
||||
routePickerButton.sendActions(for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
public func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) {
|
||||
routePickerView.removeFromSuperview()
|
||||
|
||||
self.play()
|
||||
}
|
||||
|
||||
private var observerContextAirplay = 1
|
||||
|
||||
func startObservingForAirPlayStatusChanges()
|
||||
{
|
||||
self.player?.addObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive), options: .new, context: &observerContextAirplay)
|
||||
}
|
||||
|
||||
func stopObservingForAirPlayStatusChanges()
|
||||
{
|
||||
self.player?.removeObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive))
|
||||
}
|
||||
|
||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if context == &observerContextAirplay {
|
||||
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||
}
|
||||
else {
|
||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||
}
|
||||
}
|
||||
}
|
@ -777,6 +777,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
self.controller?.present(alertController, in: .window(.root))
|
||||
}
|
||||
case "web_app_setup_closing_behavior":
|
||||
if let json = json, let _ = json["need_confirmation"] as? String {
|
||||
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user