Various video call UI improvements

This commit is contained in:
Ilya Laktyushin 2020-08-09 19:54:50 +03:00
parent 43abc10d7b
commit b7ef9fdb3f
17 changed files with 2439 additions and 2390 deletions

View File

@ -112,7 +112,7 @@ public final class AuthDataTransferSplashScreen: ViewController {
return
}
DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: strongSelf.presentationData, present: { c, a in
guard let strongSelf = self else {
return
}

View File

@ -15,6 +15,12 @@ import TelegramPresentationData
import LegacyComponents
import AccountContext
public enum DeviceAccessCameraSubject {
case video
case videoCall
}
public enum DeviceAccessMicrophoneSubject {
case audio
case video
@ -34,7 +40,7 @@ public enum DeviceAccessLocationSubject {
}
public enum DeviceAccessSubject {
case camera
case camera(DeviceAccessCameraSubject)
case microphone(DeviceAccessMicrophoneSubject)
case mediaLibrary(DeviceAccessMediaLibrarySubject)
case location(DeviceAccessLocationSubject)
@ -246,14 +252,20 @@ public final class DeviceAccess {
public static func authorizeAccess(to subject: DeviceAccessSubject, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: LocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) {
switch subject {
case .camera:
case let .camera(cameraSubject):
let status = PGCamera.cameraAuthorizationStatus()
if status == PGCameraAuthorizationStatusNotDetermined {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
Queue.mainQueue().async {
completion(response)
if !response, let presentationData = presentationData {
let text = presentationData.strings.AccessDenied_Camera
let text: String
switch cameraSubject {
case .video:
text = presentationData.strings.AccessDenied_Camera
case .videoCall:
text = presentationData.strings.AccessDenied_VideoCallCamera
}
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
openSettings()
})]), nil)

View File

@ -127,7 +127,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO
return
}
DeviceAccess.authorizeAccess(to: .camera, presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in
DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in
if value {
openCamera(cameraView, controller)
}

View File

@ -451,12 +451,14 @@ private final class SemanticStatusNodeTransitionDrawingState {
private final class SemanticStatusNodeDrawingState: NSObject {
let background: UIColor
let foreground: UIColor
let hollow: Bool
let transitionState: SemanticStatusNodeTransitionDrawingState?
let drawingState: SemanticStatusNodeStateDrawingState
init(background: UIColor, foreground: UIColor, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState) {
init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState) {
self.background = background
self.foreground = foreground
self.hollow = hollow
self.transitionState = transitionState
self.drawingState = drawingState
@ -495,6 +497,8 @@ public final class SemanticStatusNode: ASControlNode {
}
}
private let hollow: Bool
private var animator: ConstantDisplayLinkAnimator?
private var hasState: Bool = false
@ -502,9 +506,10 @@ public final class SemanticStatusNode: ASControlNode {
private var transtionContext: SemanticStatusNodeTransitionContext?
private var stateContext: SemanticStatusNodeStateContext
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor) {
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, hollow: Bool = false) {
self.backgroundNodeColor = backgroundNodeColor
self.foregroundNodeColor = foregroundNodeColor
self.hollow = hollow
self.state = .none
self.stateContext = self.state.context(current: nil)
@ -584,7 +589,7 @@ public final class SemanticStatusNode: ASControlNode {
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
}
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction))
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction))
}
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
@ -606,5 +611,10 @@ public final class SemanticStatusNode: ASControlNode {
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
}
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
if parameters.hollow {
context.setBlendMode(.clear)
context.fillEllipse(in: bounds.insetBy(dx: 7.0, dy: 7.0))
}
}
}

View File

@ -13,6 +13,7 @@ import TelegramAudio
import AccountContext
import TelegramNotices
import AppBundle
import TooltipUI
protocol CallControllerNodeProtocol: class {
var isMuted: Bool { get set }
@ -27,6 +28,7 @@ protocol CallControllerNodeProtocol: class {
var present: ((ViewController) -> Void)? { get set }
var callEnded: ((Bool) -> Void)? { get set }
var dismissedInteractively: (() -> Void)? { get set }
var dismissAllTooltips: (() -> Void)? { get set }
func updateAudioOutputs(availableOutputs: [AudioSessionOutput], currentOutput: AudioSessionOutput?)
func updateCallState(_ callState: PresentationCallState)
@ -255,6 +257,17 @@ public final class CallController: ViewController {
}
}
self.controllerNode.dismissAllTooltips = { [weak self] in
if let strongSelf = self {
strongSelf.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
}
return true
})
}
}
self.controllerNode.callEnded = { [weak self] didPresentRating in
if let strongSelf = self, !didPresentRating {
let _ = (combineLatest(strongSelf.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings]), ApplicationSpecificNotice.getCallsTabTip(accountManager: strongSelf.sharedContext.accountManager))

View File

@ -134,7 +134,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
if content.hasProgress {
if self.statusNode == nil {
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear)
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, hollow: true)
self.statusNode = statusNode
self.contentContainer.insertSubnode(statusNode, belowSubnode: self.contentNode)
statusNode.transitionToState(.progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 4.0, lineWidth: 3.0)), animated: false, completion: {})
@ -168,18 +168,19 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
let contentImage = generateImage(CGSize(width: self.largeButtonSize, height: self.largeButtonSize), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
var ellipseRect = CGRect(origin: CGPoint(), size: size)
var fillColor: UIColor = .clear
var imageColor: UIColor = .white
let imageColor: UIColor = .white
var drawOverMask = false
context.setBlendMode(.normal)
var imageScale: CGFloat = 1.0
let imageScale: CGFloat = 1.0
switch content.appearance {
case let .blurred(isFilled):
if content.hasProgress {
fillColor = .clear
imageColor = .black
drawOverMask = false
fillColor = .white
drawOverMask = true
context.setBlendMode(.copy)
ellipseRect = ellipseRect.insetBy(dx: 7.0, dy: 7.0)
} else {
if isFilled {
fillColor = .white
@ -187,8 +188,6 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
context.setBlendMode(.copy)
}
}
// let smallButtonSize: CGFloat = 60.0
// imageScale = self.largeButtonSize / smallButtonSize
case let .color(color):
switch color {
case .red:
@ -199,7 +198,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}
context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.fillEllipse(in: ellipseRect)
var image: UIImage?
@ -241,11 +240,6 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}
})
// if transition.isAnimated, let previousContent = previousContent, content.image == .camera, !previousContent.appearance.isFilled && content.appearance.isFilled {
// self.contentBackgroundNode.image = contentBackgroundImage
// self.contentBackgroundNode.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 1.25, damping: 105.0)
// }
if transition.isAnimated, let contentBackgroundImage = contentBackgroundImage, let previousContent = self.contentBackgroundNode.image {
self.contentBackgroundNode.image = contentBackgroundImage
self.contentBackgroundNode.layer.animate(from: previousContent.cgImage!, to: contentBackgroundImage.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)

View File

@ -30,6 +30,8 @@ private final class CallVideoNode: ASDisplayNode {
private let videoView: PresentationCallVideoView
private var effectView: UIVisualEffectView?
private let videoPausedNode: ImmediateTextNode
private var isBlurred: Bool = false
private var currentCornerRadius: CGFloat = 0.0
@ -41,7 +43,7 @@ private final class CallVideoNode: ASDisplayNode {
private(set) var currentOrientation: PresentationCallVideoView.Orientation
init(videoView: PresentationCallVideoView, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
init(videoView: PresentationCallVideoView, disabledText: String?, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
self.isReadyUpdated = isReadyUpdated
self.isFlippedUpdated = isFlippedUpdated
@ -53,6 +55,10 @@ private final class CallVideoNode: ASDisplayNode {
self.currentOrientation = videoView.getOrientation()
self.videoPausedNode = ImmediateTextNode()
self.videoPausedNode.alpha = 0.0
self.videoPausedNode.maximumNumberOfLines = 2
super.init()
if #available(iOS 13.0, *) {
@ -63,6 +69,11 @@ private final class CallVideoNode: ASDisplayNode {
self.videoTransformContainer.view.addSubview(self.videoView.view)
self.addSubnode(self.videoTransformContainer)
if let disabledText = disabledText {
self.videoPausedNode.attributedText = NSAttributedString(string: disabledText, font: Font.regular(17.0), textColor: .white)
self.addSubnode(self.videoPausedNode)
}
self.videoView.setOnFirstFrameReceived { [weak self] aspectRatio in
Queue.mainQueue().async {
guard let strongSelf = self else {
@ -200,6 +211,9 @@ private final class CallVideoNode: ASDisplayNode {
}
}
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: size.width - 16.0, height: 100.0))
transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - videoPausedSize.width) / 2.0), y: floor((size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
let previousVideoFrame = self.videoTransformContainer.frame
self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: size)
if transition.isAnimated && !videoFrame.height.isZero && !previousVideoFrame.height.isZero {
@ -244,6 +258,7 @@ private final class CallVideoNode: ASDisplayNode {
}
if animated {
UIView.animate(withDuration: 0.3, animations: {
self.videoPausedNode.alpha = 1.0
self.effectView?.effect = UIBlurEffect(style: light ? .light : .dark)
})
} else {
@ -252,6 +267,7 @@ private final class CallVideoNode: ASDisplayNode {
} else if let effectView = self.effectView {
self.effectView = nil
UIView.animate(withDuration: 0.3, animations: {
self.videoPausedNode.alpha = 0.0
effectView.effect = nil
}, completion: { [weak effectView] _ in
effectView?.removeFromSuperview()
@ -327,7 +343,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
private let backButtonArrowNode: ASImageNode
private let backButtonNode: HighlightableButtonNode
private let statusNode: CallControllerStatusNode
private let videoPausedNode: ImmediateTextNode
private let toastNode: CallControllerToastContainerNode
private let buttonsNode: CallControllerButtonsNode
private var keyPreviewNode: CallControllerKeyPreviewNode?
@ -343,6 +358,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
var isMuted: Bool = false {
didSet {
self.buttonsNode.isMuted = self.isMuted
self.updateToastContent()
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
@ -364,6 +380,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
var callEnded: ((Bool) -> Void)?
var dismissedInteractively: (() -> Void)?
var present: ((ViewController) -> Void)?
var dismissAllTooltips: (() -> Void)?
private var toastContent: CallControllerToastContent?
private var displayToastsAfterTimestamp: Double?
@ -416,9 +433,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.statusNode = CallControllerStatusNode()
self.videoPausedNode = ImmediateTextNode()
self.videoPausedNode.alpha = 0.0
self.buttonsNode = CallControllerButtonsNode(strings: self.presentationData.strings)
self.toastNode = CallControllerToastContainerNode(strings: self.presentationData.strings)
self.keyButtonNode = CallControllerKeyButton()
@ -451,7 +465,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.dimNode)
self.containerNode.addSubnode(self.statusNode)
self.containerNode.addSubnode(self.videoPausedNode)
self.containerNode.addSubnode(self.buttonsNode)
self.containerNode.addSubnode(self.toastNode)
self.containerNode.addSubnode(self.keyButtonNode)
@ -553,7 +566,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
func displayCameraTooltip() {
guard let location = self.buttonsNode.videoButtonFrame().flatMap({ frame -> CGRect in
guard self.pictureInPictureTransitionFraction.isZero, let location = self.buttonsNode.videoButtonFrame().flatMap({ frame -> CGRect in
return self.buttonsNode.view.convert(frame, to: self.view)
}) else {
return
@ -605,8 +618,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
self.videoPausedNode.attributedText = NSAttributedString(string: self.presentationData.strings.Call_RemoteVideoPaused(peer.compactDisplayTitle).0, font: Font.regular(17.0), textColor: .white)
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
@ -671,9 +682,11 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
strongSelf.expandedVideoNode = incomingVideoNode
strongSelf.updateButtonsMode(transition: .animated(duration: 0.4, curve: .spring))
strongSelf.updateDimVisibility()
}
let incomingVideoNode = CallVideoNode(videoView: incomingVideoView, assumeReadyAfterTimeout: false, isReadyUpdated: {
let incomingVideoNode = CallVideoNode(videoView: incomingVideoView, disabledText: strongSelf.presentationData.strings.Call_RemoteVideoPaused(strongSelf.peer?.compactDisplayTitle ?? "").0, assumeReadyAfterTimeout: false, isReadyUpdated: {
if delayUntilInitialized {
Queue.mainQueue().after(0.1, {
applyNode()
@ -752,9 +765,11 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
strongSelf.containerNode.insertSubnode(outgoingVideoNode, belowSubnode: strongSelf.dimNode)
}
strongSelf.updateButtonsMode(transition: .animated(duration: 0.4, curve: .spring))
strongSelf.updateDimVisibility()
}
let outgoingVideoNode = CallVideoNode(videoView: outgoingVideoView, assumeReadyAfterTimeout: true, isReadyUpdated: {
let outgoingVideoNode = CallVideoNode(videoView: outgoingVideoView, disabledText: nil, assumeReadyAfterTimeout: true, isReadyUpdated: {
if delayUntilInitialized {
Queue.mainQueue().after(0.4, {
applyNode()
@ -832,15 +847,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
isActive = true
}
incomingVideoNode.updateIsBlurred(isBlurred: !isActive)
if isActive != self.videoPausedNode.alpha.isZero {
if isActive {
self.videoPausedNode.alpha = 0.0
self.videoPausedNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
} else {
self.videoPausedNode.alpha = 1.0
self.videoPausedNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
}
}
@ -935,6 +941,34 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
self.updateToastContent()
self.updateButtonsMode()
self.updateDimVisibility()
if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
self.displayedCameraTooltip = true
self.displayedCameraConfirmation = true
}
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
self.displayedCameraTooltip = true
Queue.mainQueue().after(2.0) {
self.displayCameraTooltip()
}
}
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
let presentRating = reportRating || self.forceReportRating
if presentRating {
self.presentCallRating?(callId)
}
self.callEnded?(presentRating)
}
}
private func updateToastContent() {
guard let callState = self.callState else {
return
}
if case .terminating = callState.state {
} else if case .terminated = callState.state {
} else {
@ -961,27 +995,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
self.toastContent = toastContent
}
self.updateButtonsMode()
self.updateDimVisibility()
if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
self.displayedCameraTooltip = true
}
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
self.displayedCameraTooltip = true
Queue.mainQueue().after(2.0) {
self.displayCameraTooltip()
}
}
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
let presentRating = reportRating || self.forceReportRating
if presentRating {
self.presentCallRating?(callId)
}
self.callEnded?(presentRating)
}
}
private func updateDimVisibility(transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)) {
@ -998,8 +1011,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
if visible != currentVisible {
let color = visible ? UIColor(rgb: 0x000000, alpha: 0.3) : UIColor.clear
let image: UIImage? = visible ? nil : generateGradientImage(size: CGSize(width: 1.0, height: 640.0), colors: [UIColor.black.withAlphaComponent(0.3), UIColor.clear, UIColor.clear, UIColor.black.withAlphaComponent(0.3)], locations: [0.0, 0.22, 0.7, 1.0])
if transition.isAnimated {
UIView.transition(with: self.dimNode.view, duration: 0.3, options: .transitionCrossDissolve, animations: {
if case let .animated(duration, _) = transition {
UIView.transition(with: self.dimNode.view, duration: duration, options: .transitionCrossDissolve, animations: {
self.dimNode.backgroundColor = color
self.dimNode.image = image
}, completion: nil)
@ -1007,8 +1020,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.dimNode.backgroundColor = color
self.dimNode.image = image
}
self.statusNode.isHidden = !visible
}
self.statusNode.setVisible(visible || self.keyPreviewNode != nil, transition: transition)
}
private var buttonsTerminationMode: CallControllerButtonsMode?
@ -1299,9 +1312,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusOffset), size: CGSize(width: layout.size.width, height: statusHeight)))
transition.updateAlpha(node: self.statusNode, alpha: overlayAlpha)
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: 100.0))
transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - videoPausedSize.width) / 2.0), y: floor((layout.size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
transition.updateFrame(node: self.toastNode, frame: CGRect(origin: CGPoint(x: 0.0, y: toastOriginY), size: CGSize(width: layout.size.width, height: toastHeight)))
transition.updateFrame(node: self.buttonsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonsOriginY), size: CGSize(width: layout.size.width, height: buttonsHeight)))
transition.updateAlpha(node: self.buttonsNode, alpha: overlayAlpha)
@ -1424,6 +1434,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.keyButtonNode.isHidden = true
keyPreviewNode.animateIn(from: self.keyButtonNode.frame, fromNode: self.keyButtonNode)
}
self.updateDimVisibility()
}
}
@ -1434,6 +1446,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self?.keyButtonNode.isHidden = false
keyPreviewNode?.removeFromSupernode()
})
self.updateDimVisibility()
} else if self.hasVideoNodes {
if let (layout, navigationHeight) = self.validLayout {
self.pictureInPictureTransitionFraction = 1.0
@ -1669,7 +1682,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
if self.pictureInPictureTransitionFraction.isZero, let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(location), expandedVideoNode.frame != minimizedVideoNode.frame {
self.minimizedVideoInitialPosition = minimizedVideoNode.position
} else if let _ = self.minimizedVideoNode {
} else if self.hasVideoNodes {
self.minimizedVideoInitialPosition = nil
if !self.pictureInPictureTransitionFraction.isZero {
self.pictureInPictureGestureState = .dragging(initialPosition: self.containerTransformationNode.position, draggingPosition: self.containerTransformationNode.position)
@ -1679,6 +1692,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} else {
self.pictureInPictureGestureState = .none
}
self.dismissAllTooltips?()
case .changed:
if let minimizedVideoNode = self.minimizedVideoNode, let minimizedVideoInitialPosition = self.minimizedVideoInitialPosition {
let translation = recognizer.translation(in: self.view)

View File

@ -133,6 +133,12 @@ final class CallControllerStatusNode: ASDisplayNode {
self.statusTimer?.invalidate()
}
func setVisible(_ visible: Bool, transition: ContainedViewLayoutTransition) {
let alpha: CGFloat = visible ? 1.0 : 0.0
transition.updateAlpha(node: self.titleNode, alpha: alpha)
transition.updateAlpha(node: self.statusContainerNode, alpha: alpha)
}
func updateLayout(constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
self.validLayoutWidth = constrainedWidth

View File

@ -273,7 +273,6 @@ private class CallControllerToastItemNode: ASDisplayNode {
self.iconNode.image = image
}
if previousContent?.text != content.text {
self.textNode.attributedText = NSAttributedString(string: content.text, font: Font.regular(17.0), textColor: .white)
let iconSize = CGSize(width: 44.0, height: 28.0)
@ -291,7 +290,6 @@ private class CallControllerToastItemNode: ASDisplayNode {
self.currentHeight = backgroundSize.height
}
}
return self.currentHeight ?? 28.0
}

View File

@ -66,6 +66,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
var callEnded: ((Bool) -> Void)?
var dismissedInteractively: (() -> Void)?
var present: ((ViewController) -> Void)?
var dismissAllTooltips: (() -> Void)?
init(sharedContext: SharedAccountContext, account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall) {
self.sharedContext = sharedContext

View File

@ -789,7 +789,7 @@ public final class PresentationCallImpl: PresentationCall {
}
if value {
if strongSelf.isVideo {
DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()

View File

@ -374,7 +374,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
openSettings()
}, { value in
if isVideo && value {
DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
@ -450,7 +450,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
openSettings()
}, { value in
if isVideo && value {
DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()

View File

@ -4022,7 +4022,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
if isVideo {
DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: strongSelf.presentationData, present: { c, a in
self?.present(c, in: .window(.root), with: a)
}, openSettings: {
self?.context.sharedContext.applicationBindings.openSettings()

View File

@ -259,7 +259,7 @@ final class WalletContextImpl: WalletContext {
func authorizeAccessToCamera(completion: @escaping () -> Void) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: presentationData, present: { c, a in
c.presentationArguments = a
self.context.sharedContext.mainWindow?.present(c, on: .root)
}, openSettings: { [weak self] in

@ -1 +1 @@
Subproject commit fd1892ffde13fdba03b2d6caca08b4c210ab9dcd
Subproject commit da1160dfbf4ac4b0dee65d481bd9c634932cd5a2