@@ -38,6 +38,7 @@ import MultilineTextComponent
3838import BundleIconComponent
3939import VideoPlaybackControlsComponent
4040import PhotoResources
41+ import GlassBackgroundComponent
4142
4243public enum UniversalVideoGalleryItemContentInfo {
4344 case message( Message , GalleryMediaSubject ? )
@@ -893,7 +894,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
893894 private let statusButtonNode : HighlightableButtonNode
894895 private let statusNode : RadialStatusNode
895896 private var statusNodeShouldBeHidden = true
896- private var livePhotoIconNode : ASImageNode ?
897+ private var livePhotoButton : LivePhotoButton ?
897898
898899 private let playbackControls = ComponentView < Empty > ( )
899900
@@ -963,9 +964,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
963964 private var activeEdgeRateIndicator : ComponentView < Empty > ?
964965
965966 private var isAnimatingOut : Bool = false
967+
966968 private var isLivePhoto = false
967969 private var didAutoplayLivePhotoOnce = false
968- private var isPlayingLivePhotoByHolding = false
970+ private var isLivePhotoPlaybackActive = false
971+ private var isLivePhotoGestureActive = false
969972
970973 init ( context: AccountContext , presentationData: PresentationData , performAction: @escaping ( GalleryControllerInteractionTapAction ) -> Void , openActionOptions: @escaping ( GalleryControllerInteractionTapAction , Message ) -> Void , present: @escaping ( ViewController , Any ? ) -> Void ) {
971974 self . context = context
@@ -1275,6 +1278,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
12751278 let transition = ComponentTransition ( transition)
12761279 transition. setFrame ( view: playbackControlsView, frame: playbackControlsFrame)
12771280 }
1281+
1282+ if let livePhotoButton = self . livePhotoButton {
1283+ let livePhotoButtonSize = livePhotoButton. update ( isPlaying: self . isLivePhotoPlaybackActive)
1284+ if livePhotoButton. superview == nil {
1285+ self . view. addSubview ( livePhotoButton)
1286+ }
1287+ transition. updateFrame ( view: livePhotoButton, frame: CGRect ( origin: CGPoint ( x: 16.0 , y: max ( navigationBarHeight, layout. statusBarHeight ?? 0.0 ) + 10.0 ) , size: livePhotoButtonSize) )
1288+ }
12781289
12791290 if let pictureInPictureNode = self . pictureInPictureNode {
12801291 if let item = self . item {
@@ -1374,10 +1385,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
13741385 self . statusButtonNode. isHidden = true
13751386 }
13761387
1388+ self . resetLivePhotoPlayback ( )
13771389 self . dismissOnOrientationChange = item. landscape
13781390 self . isLivePhoto = false
13791391 self . didAutoplayLivePhotoOnce = false
1380- self . isPlayingLivePhotoByHolding = false
1392+ self . isLivePhotoPlaybackActive = false
13811393
13821394 var hasLinkedStickers = false
13831395 if let content = item. content as? NativeVideoContent {
@@ -1716,7 +1728,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
17161728 initialBuffering = true
17171729 }
17181730 isPaused = !whilePlaying
1719- if strongSelf. isPlayingLivePhotoByHolding {
1731+ if strongSelf. isLivePhotoPlaybackActive {
17201732 isPaused = false
17211733 }
17221734 var isStreaming = false
@@ -1961,7 +1973,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
19611973 }
19621974
19631975 if strongSelf. isLivePhoto {
1964- if !strongSelf. isPlayingLivePhotoByHolding {
1976+ if !strongSelf. isLivePhotoPlaybackActive {
19651977 strongSelf. setLivePhotoVideoVisible ( false , animated: true )
19661978 }
19671979 } else if let snapshotView = videoNode? . view. snapshotView ( afterScreenUpdates: false ) {
@@ -1973,7 +1985,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
19731985
19741986 if !strongSelf. isLivePhoto {
19751987 videoNode? . seek ( 0.0 )
1976- } else if strongSelf. isPlayingLivePhotoByHolding {
1988+ } else if strongSelf. isLivePhotoPlaybackActive {
19771989 videoNode? . playOnceWithSound ( playAndRecord: false , actionAtEnd: . loop)
19781990 }
19791991
@@ -2059,22 +2071,29 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
20592071 }
20602072
20612073 if self . isLivePhoto {
2062- let livePhotoIconNode : ASImageNode
2063- if let current = self . livePhotoIconNode {
2064- livePhotoIconNode = current
2074+ let livePhotoButton : LivePhotoButton
2075+ if let current = self . livePhotoButton {
2076+ livePhotoButton = current
20652077 } else {
2066- livePhotoIconNode = ASImageNode ( )
2067- livePhotoIconNode. image = generateTintedImage ( image: UIImage ( bundleImageName: " Media Editor/LiveOn " ) , color: . white)
2068- //self.addSubnode(livePhotoIconNode)
2069- self . livePhotoIconNode = livePhotoIconNode
2070- }
2071-
2072- if let icon = livePhotoIconNode. image {
2073- livePhotoIconNode. frame = CGRect ( origin: CGPoint ( x: 8.0 , y: 8.0 ) , size: icon. size)
2074- }
2075- } else if let livePhotoIconNode = self . livePhotoIconNode {
2076- self . livePhotoIconNode = nil
2077- livePhotoIconNode. removeFromSupernode ( )
2078+ livePhotoButton = LivePhotoButton ( context: self . context)
2079+ livePhotoButton. pressed = { [ weak self] in
2080+ guard let self else {
2081+ return
2082+ }
2083+ self . updateLivePhotoPlayback ( isActive: !self . isLivePhotoPlaybackActive, animated: true )
2084+ }
2085+ self . livePhotoButton = livePhotoButton
2086+ self . view. addSubview ( livePhotoButton)
2087+ }
2088+ // if livePhotoButton.superview == nil {
2089+ //
2090+ // }
2091+ // if let validLayout = self.validLayout {
2092+ // self.containerLayoutUpdated(validLayout.layout, navigationBarHeight: validLayout.navigationBarHeight, transition: .immediate)
2093+ // }
2094+ } else if let livePhotoButton = self . livePhotoButton {
2095+ self . livePhotoButton = nil
2096+ livePhotoButton. removeFromSuperview ( )
20782097 }
20792098 }
20802099
@@ -2169,24 +2188,29 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
21692188 }
21702189 }
21712190
2172- private func updateLivePhotoHoldPlayback( isActive: Bool , animated: Bool ) {
2173- guard self . isLivePhoto, let videoNode = self . videoNode else {
2174- return
2175- }
2176- guard self . isPlayingLivePhotoByHolding != isActive else {
2191+ private func updateLivePhotoPlayback( isActive: Bool , animated: Bool ) {
2192+ guard self . isLivePhoto, self . isLivePhotoPlaybackActive != isActive, let videoNode = self . videoNode else {
21772193 return
21782194 }
2179-
2180- self . isPlayingLivePhotoByHolding = isActive
2195+
2196+ self . isLivePhotoPlaybackActive = isActive
21812197 if isActive {
21822198 self . setLivePhotoVideoVisible ( true , animated: animated)
21832199 videoNode. seek ( 0.0 )
2184- videoNode. playOnceWithSound ( playAndRecord: false )
2200+ videoNode. playOnceWithSound ( playAndRecord: false , actionAtEnd : . loop )
21852201 } else {
21862202 videoNode. pause ( )
21872203 videoNode. seek ( 0.0 )
21882204 self . setLivePhotoVideoVisible ( false , animated: animated)
21892205 }
2206+
2207+ if let validLayout = self . validLayout {
2208+ self . containerLayoutUpdated ( validLayout. layout, navigationBarHeight: validLayout. navigationBarHeight, transition: animated ? . animated( duration: 0.2 , curve: . easeInOut) : . immediate)
2209+ }
2210+ }
2211+
2212+ private func resetLivePhotoPlayback( ) {
2213+ self . updateLivePhotoPlayback ( isActive: false , animated: false )
21902214 }
21912215
21922216 override func centralityUpdated( isCentral: Bool ) {
@@ -2228,7 +2252,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
22282252 }
22292253 }
22302254 } else {
2231- self . updateLivePhotoHoldPlayback ( isActive : false , animated : false )
2255+ self . resetLivePhotoPlayback ( )
22322256 self . isPlayingPromise. set ( false )
22332257 self . isPlaying = false
22342258
@@ -2265,17 +2289,19 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
22652289 if self . skipInitialPause {
22662290 self . skipInitialPause = false
22672291 } else {
2268- self . updateLivePhotoHoldPlayback ( isActive : false , animated : false )
2292+ self . resetLivePhotoPlayback ( )
22692293 self . ignorePauseStatus = true
22702294 videoNode. pause ( )
22712295 videoNode. seek ( 0.0 )
22722296 }
22732297 } else {
2274- self . updateLivePhotoHoldPlayback ( isActive : false , animated : false )
2298+ self . resetLivePhotoPlayback ( )
22752299 if let status = self . playerStatusValue {
22762300 self . maybeStorePlaybackStatus ( status: status)
22772301 }
2278- videoNode. continuePlayingWithoutSound ( )
2302+ if !self . isLivePhoto {
2303+ videoNode. continuePlayingWithoutSound ( )
2304+ }
22792305 }
22802306 self . updateDisplayPlaceholder ( )
22812307 } else if !item. fromPlayingVideo {
@@ -2382,7 +2408,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
23822408
23832409 private var actionAtEnd : MediaPlayerPlayOnceWithSoundActionAtEnd {
23842410 if self . isLivePhoto {
2385- return self . isPlayingLivePhotoByHolding ? . loop : . stop
2411+ return self . isLivePhotoPlaybackActive ? . loop : . stop
23862412 }
23872413 if let item = self . item {
23882414 if !item. isSecret, let content = item. content as? NativeVideoContent , content. duration <= 30 {
@@ -4082,7 +4108,13 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
40824108 return
40834109 }
40844110 if let edge, case . middle = edge {
4085- self . updateLivePhotoHoldPlayback ( isActive: true , animated: true )
4111+ guard self . isLivePhoto else {
4112+ return
4113+ }
4114+ if !self . isLivePhotoPlaybackActive {
4115+ self . updateLivePhotoPlayback ( isActive: true , animated: true )
4116+ }
4117+ self . isLivePhotoGestureActive = true
40864118 } else if let edge, case . right = edge {
40874119 let effectiveRate : Double
40884120 if let current = self . activeEdgeRateState {
@@ -4097,7 +4129,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
40974129 }
40984130 videoNode. setBaseRate ( effectiveRate)
40994131 } else {
4100- self . updateLivePhotoHoldPlayback ( isActive: false , animated: true )
4132+ if self . isLivePhotoGestureActive {
4133+ self . updateLivePhotoPlayback ( isActive: false , animated: true )
4134+ }
4135+ self . isLivePhotoGestureActive = false
4136+
41014137 if let ( initialRate, _) = self . activeEdgeRateState {
41024138 self . activeEdgeRateState = nil
41034139 videoNode. setBaseRate ( initialRate)
@@ -4154,3 +4190,87 @@ final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
41544190private func normalizeValue( _ value: CGFloat ) -> CGFloat {
41554191 return round ( value * 10.0 ) / 10.0
41564192}
4193+
4194+ private final class LivePhotoButton : UIView {
4195+ private let context : AccountContext
4196+
4197+ private let backgroundView : GlassBackgroundView
4198+ private let icon = ComponentView < Empty > ( )
4199+ private let label = ComponentView < Empty > ( )
4200+ private let button = HighlightTrackingButton ( )
4201+
4202+ override func point( inside point: CGPoint , with event: UIEvent ? ) -> Bool {
4203+ return self . bounds. insetBy ( dx: - 16.0 , dy: - 16.0 ) . contains ( point)
4204+ }
4205+
4206+ var pressed : ( ) -> Void = { }
4207+
4208+ init ( context: AccountContext ) {
4209+ self . context = context
4210+
4211+ self . backgroundView = GlassBackgroundView ( )
4212+
4213+ super. init ( frame: . zero)
4214+
4215+ self . addSubview ( self . backgroundView)
4216+ self . backgroundView. contentView. addSubview ( self . button)
4217+
4218+ self . button. addTarget ( self , action: #selector( self . buttonPressed) , for: . touchUpInside)
4219+ }
4220+
4221+ required init ? ( coder: NSCoder ) {
4222+ preconditionFailure ( )
4223+ }
4224+
4225+ @objc private func buttonPressed( ) {
4226+ self . pressed ( )
4227+ }
4228+
4229+ func update( isPlaying: Bool = false ) -> CGSize {
4230+ let presentationData = self . context. sharedContext. currentPresentationData. with { $0 }
4231+
4232+ let iconName : String = isPlaying ? " Media Gallery/LivePhotoPlaying " : " Media Gallery/LivePhotoPlay "
4233+ let labelText : String = presentationData. strings. Gallery_Live
4234+
4235+ let iconSize = self . icon. update (
4236+ transition: . immediate,
4237+ component: AnyComponent (
4238+ BundleIconComponent ( name: iconName, tintColor: . white)
4239+ ) ,
4240+ environment: { } ,
4241+ containerSize: CGSize ( width: 18.0 , height: 18.0 )
4242+ )
4243+ let iconFrame = CGRect ( origin: CGPoint ( x: 8.0 , y: floorToScreenPixels ( ( 30.0 - iconSize. height) / 2.0 ) ) , size: iconSize)
4244+ if let iconView = self . icon. view {
4245+ if iconView. superview == nil {
4246+ iconView. isUserInteractionEnabled = false
4247+ self . backgroundView. contentView. addSubview ( iconView)
4248+ }
4249+ iconView. frame = iconFrame
4250+ }
4251+
4252+ let labelSize = self . label. update (
4253+ transition: . immediate,
4254+ component: AnyComponent (
4255+ Text ( text: labelText. uppercased ( ) , font: Font . regular ( 12.0 ) , color: . white)
4256+ ) ,
4257+ environment: { } ,
4258+ containerSize: CGSize ( width: 200.0 , height: 18.0 )
4259+ )
4260+ let labelFrame = CGRect ( origin: CGPoint ( x: 28.0 , y: floorToScreenPixels ( ( 30.0 - labelSize. height) / 2.0 ) ) , size: labelSize)
4261+ if let labelView = self . label. view {
4262+ if labelView. superview == nil {
4263+ labelView. isUserInteractionEnabled = false
4264+ self . backgroundView. contentView. addSubview ( labelView)
4265+ }
4266+ labelView. frame = labelFrame
4267+ }
4268+
4269+ let size = CGSize ( width: 21.0 + labelSize. width + 18.0 , height: 30.0 )
4270+ self . backgroundView. update ( size: size, cornerRadius: size. height * 0.5 , isDark: true , tintColor: . init( kind: . panel) , isInteractive: true , transition: . immediate)
4271+ self . backgroundView. frame = CGRect ( origin: . zero, size: size)
4272+ self . button. frame = CGRect ( origin: . zero, size: size) . insetBy ( dx: - 16.0 , dy: - 16.0 )
4273+
4274+ return size
4275+ }
4276+ }
0 commit comments