mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various video call UI improvements
This commit is contained in:
parent
43abc10d7b
commit
b7ef9fdb3f
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -272,25 +272,23 @@ private class CallControllerToastItemNode: ASDisplayNode {
|
||||
} else {
|
||||
self.iconNode.image = image
|
||||
}
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: content.text, font: Font.regular(17.0), textColor: .white)
|
||||
|
||||
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)
|
||||
let iconSpacing: CGFloat = 2.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - inset * 2.0 - iconSize.width - iconSpacing, height: 100.0))
|
||||
|
||||
let backgroundSize = CGSize(width: iconSize.width + iconSpacing + textSize.width + 6.0 * 2.0, height: max(28.0, textSize.height + 4.0 * 2.0))
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floor((width - backgroundSize.width) / 2.0), y: 0.0), size: backgroundSize)
|
||||
|
||||
transition.updateFrame(node: self.clipNode, frame: backgroundFrame)
|
||||
transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(), size: iconSize)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: iconSize.width + iconSpacing, y: 4.0), size: textSize)
|
||||
|
||||
self.currentHeight = backgroundSize.height
|
||||
}
|
||||
let iconSize = CGSize(width: 44.0, height: 28.0)
|
||||
let iconSpacing: CGFloat = 2.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - inset * 2.0 - iconSize.width - iconSpacing, height: 100.0))
|
||||
|
||||
let backgroundSize = CGSize(width: iconSize.width + iconSpacing + textSize.width + 6.0 * 2.0, height: max(28.0, textSize.height + 4.0 * 2.0))
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floor((width - backgroundSize.width) / 2.0), y: 0.0), size: backgroundSize)
|
||||
|
||||
transition.updateFrame(node: self.clipNode, frame: backgroundFrame)
|
||||
transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(), size: iconSize)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: iconSize.width + iconSpacing, y: 4.0), size: textSize)
|
||||
|
||||
self.currentHeight = backgroundSize.height
|
||||
}
|
||||
return self.currentHeight ?? 28.0
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -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()
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user