Skip to content

Commit 8b17941

Browse files
committed
Call UI improvements
1 parent edeb1a6 commit 8b17941

8 files changed

Lines changed: 107 additions & 64 deletions

File tree

‎Telegram/Telegram-iOS/en.lproj/Localizable.strings‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5639,7 +5639,7 @@ Any member of this group will be able to see messages in the channel.";
56395639
"PrivacySettings.AutoArchive" = "Archive and Mute";
56405640
"PrivacySettings.AutoArchiveInfo" = "Automatically archive and mute new chats, groups and channels from non-contacts.";
56415641

5642-
"Call.RemoteVideoPaused" = "%@'s video paused";
5642+
"Call.RemoteVideoPaused" = "%@'s video is paused";
56435643

56445644
"Settings.SetProfilePhotoOrVideo" = "Set Photo or Video";
56455645
"Settings.SetNewProfilePhotoOrVideo" = "Set New Photo or Video";

‎submodules/AccountContext/Sources/PresentationCallManager.swift‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@ public struct PresentationCallState: Equatable {
4242
case reconnecting(Double, Int32?, Data)
4343
case terminating
4444
case terminated(CallId?, CallSessionTerminationReason?, Bool)
45-
46-
public var isOrWasActive: Bool {
47-
switch self {
48-
case .active, .terminating, .terminated:
49-
return true
50-
default:
51-
return false
52-
}
53-
}
5445
}
5546

5647
public enum VideoState: Equatable {

‎submodules/TelegramCallsUI/Sources/CallController.swift‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ public final class CallController: ViewController {
167167
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
168168
var items: [ActionSheetItem] = []
169169
for output in availableOutputs {
170+
if hasMute, case .builtin = output {
171+
continue
172+
}
170173
let title: String
171174
var icon: UIImage?
172175
switch output {

‎submodules/TelegramCallsUI/Sources/CallControllerKeyPreviewNode.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ final class CallControllerKeyPreviewNode: ASDisplayNode {
8686
self.keyTextNode.layer.animatePosition(from: self.keyTextNode.layer.position, to: CGPoint(x: rect.midX + 2.0, y: rect.midY), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
8787
completion()
8888
})
89-
self.keyTextNode.layer.animateScale(from: 1.0, to: rect.size.width / self.keyTextNode.frame.size.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
89+
self.keyTextNode.layer.animateScale(from: 1.0, to: rect.size.width / (self.keyTextNode.frame.size.width - 2.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
9090

9191
self.infoTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
9292

‎submodules/TelegramCallsUI/Sources/CallControllerNode.swift‎

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ private final class CallVideoNode: ASDisplayNode {
263263
if withBackground {
264264
self.backgroundColor = .black
265265
}
266-
UIView.transition(with: self.videoTransformContainer.view, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
266+
UIView.transition(with: withBackground ? self.videoTransformContainer.view : self.view, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
267267
UIView.performWithoutAnimation {
268268
self.updateIsBlurred(isBlurred: true, light: true, animated: false)
269269
}
@@ -315,6 +315,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
315315
private var isRequestingVideo: Bool = false
316316
private var animateRequestedVideoOnce: Bool = false
317317

318+
private var displayedCameraConfirmation: Bool = false
318319
private var displayedCameraTooltip: Bool = false
319320

320321
private var expandedVideoNode: CallVideoNode?
@@ -365,6 +366,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
365366
var present: ((ViewController) -> Void)?
366367

367368
private var toastContent: CallControllerToastContent?
369+
private var displayToastsAfterTimestamp: Double?
370+
368371
private var buttonsMode: CallControllerButtonsMode?
369372

370373
private var isUIHidden: Bool = false
@@ -460,7 +463,10 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
460463
}
461464

462465
self.buttonsNode.speaker = { [weak self] in
463-
self?.beginAudioOuputSelection?(true)
466+
guard let strongSelf = self else {
467+
return
468+
}
469+
strongSelf.beginAudioOuputSelection?(strongSelf.hasVideoNodes)
464470
}
465471

466472
self.buttonsNode.acceptOrEnd = { [weak self] in
@@ -491,6 +497,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
491497
case .active:
492498
if strongSelf.outgoingVideoNodeValue == nil {
493499
let proceed = {
500+
strongSelf.displayedCameraConfirmation = true
494501
switch callState.videoState {
495502
case .inactive:
496503
strongSelf.isRequestingVideo = true
@@ -501,9 +508,13 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
501508
strongSelf.call.requestVideo()
502509
}
503510

504-
strongSelf.present?(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.Call_CameraConfirmationText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Call_CameraConfirmationConfirm, action: {
511+
if strongSelf.displayedCameraConfirmation {
505512
proceed()
506-
})]))
513+
} else {
514+
strongSelf.present?(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.Call_CameraConfirmationText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Call_CameraConfirmationConfirm, action: {
515+
proceed()
516+
})]))
517+
}
507518
} else {
508519
strongSelf.call.disableVideo()
509520
}
@@ -517,8 +528,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
517528
return
518529
}
519530
strongSelf.disableActionsUntilTimestamp = CACurrentMediaTime() + 1.0
520-
if let outgoingVideoNode = strongSelf.outgoingVideoNodeValue, let (layout, _) = strongSelf.validLayout {
521-
outgoingVideoNode.flip(withBackground: outgoingVideoNode.frame.width == layout.size.width)
531+
if let outgoingVideoNode = strongSelf.outgoingVideoNodeValue {
532+
outgoingVideoNode.flip(withBackground: outgoingVideoNode !== strongSelf.minimizedVideoNode)
522533
}
523534
strongSelf.call.switchVideoCamera()
524535
if let _ = strongSelf.outgoingVideoNodeValue {
@@ -924,30 +935,40 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
924935
}
925936
}
926937

927-
var toastContent: CallControllerToastContent = []
928-
if callState.state.isOrWasActive {
929-
if self.hasVideoNodes && [.inactive, .paused].contains(callState.remoteVideoState) {
930-
toastContent.insert(.camera)
931-
}
932-
if case .muted = callState.remoteAudioState {
933-
toastContent.insert(.microphone)
938+
if case .terminating = callState.state {
939+
} else if case .terminated = callState.state {
940+
} else {
941+
var toastContent: CallControllerToastContent = []
942+
if case .active = callState.state {
943+
if let displayToastsAfterTimestamp = self.displayToastsAfterTimestamp {
944+
if CACurrentMediaTime() > displayToastsAfterTimestamp {
945+
if case .inactive = callState.remoteVideoState, self.hasVideoNodes {
946+
toastContent.insert(.camera)
947+
}
948+
if case .muted = callState.remoteAudioState {
949+
toastContent.insert(.microphone)
950+
}
951+
if case .low = callState.remoteBatteryLevel {
952+
toastContent.insert(.battery)
953+
}
954+
}
955+
} else {
956+
self.displayToastsAfterTimestamp = CACurrentMediaTime() + 2.0
957+
}
934958
}
935-
if case .low = callState.remoteBatteryLevel {
936-
toastContent.insert(.battery)
959+
if self.isMuted, let (availableOutputs, _) = self.audioOutputState, availableOutputs.count > 2 {
960+
toastContent.insert(.mute)
937961
}
962+
self.toastContent = toastContent
938963
}
939-
if self.isMuted, let (availableOutputs, _) = self.audioOutputState, availableOutputs.count > 2 {
940-
toastContent.insert(.mute)
941-
}
942-
self.toastContent = toastContent
943964

944965
self.updateButtonsMode()
945966
self.updateDimVisibility()
946967

947968
if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
948969
self.displayedCameraTooltip = true
949970
}
950-
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && toastContent.isEmpty {
971+
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
951972
self.displayedCameraTooltip = true
952973
Queue.mainQueue().after(2.0) {
953974
self.displayCameraTooltip()
@@ -1098,21 +1119,19 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
10981119
}
10991120

11001121
private func calculatePreviewVideoRect(layout: ContainerViewLayout, navigationHeight: CGFloat) -> CGRect {
1101-
var uiDisplayTransition: CGFloat = self.isUIHidden ? 0.0 : 1.0
1102-
uiDisplayTransition *= 1.0 - self.pictureInPictureTransitionFraction
1103-
11041122
let buttonsHeight: CGFloat = self.buttonsNode.bounds.height
11051123
let toastHeight: CGFloat = self.toastNode.bounds.height
1124+
let toastInset = (toastHeight > 0.0 ? toastHeight + 22.0 : 0.0)
11061125

11071126
var fullInsets = layout.insets(options: .statusBar)
1108-
1127+
11091128
var cleanInsets = fullInsets
1110-
cleanInsets.bottom = layout.intrinsicInsets.bottom
1129+
cleanInsets.bottom = max(layout.intrinsicInsets.bottom, 20.0) + toastInset
11111130
cleanInsets.left = 20.0
11121131
cleanInsets.right = 20.0
11131132

11141133
fullInsets.top += 44.0 + 8.0
1115-
fullInsets.bottom = buttonsHeight + toastHeight + 27.0
1134+
fullInsets.bottom = buttonsHeight + 22.0 + toastInset
11161135
fullInsets.left = 20.0
11171136
fullInsets.right = 20.0
11181137

@@ -1179,8 +1198,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
11791198
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
11801199
self.validLayout = (layout, navigationBarHeight)
11811200

1182-
var uiDisplayTransition: CGFloat = self.isUIHidden ? 0.0 : 1.0
1183-
uiDisplayTransition *= 1.0 - self.pictureInPictureTransitionFraction
1201+
var isUIHidden = self.isUIHidden
1202+
switch self.callState?.state {
1203+
case .terminated, .terminating:
1204+
isUIHidden = false
1205+
default:
1206+
break
1207+
}
1208+
1209+
var uiDisplayTransition: CGFloat = isUIHidden ? 0.0 : 1.0
1210+
let pipTransitionAlpha: CGFloat = 1.0 - self.pictureInPictureTransitionFraction
1211+
uiDisplayTransition *= pipTransitionAlpha
11841212

11851213
let previousVideoButtonFrame = self.buttonsNode.videoButtonFrame().flatMap { frame -> CGRect in
11861214
return self.buttonsNode.view.convert(frame, to: self.view)
@@ -1193,15 +1221,22 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
11931221
buttonsHeight = 0.0
11941222
}
11951223
let defaultButtonsOriginY = layout.size.height - buttonsHeight
1196-
let buttonsOriginY = interpolate(from: layout.size.height + 10.0, to: defaultButtonsOriginY, value: uiDisplayTransition)
1224+
let buttonsCollapsedOriginY = self.pictureInPictureTransitionFraction > 0.0 ? layout.size.height + 30.0 : layout.size.height + 10.0
1225+
let buttonsOriginY = interpolate(from: buttonsCollapsedOriginY, to: defaultButtonsOriginY, value: uiDisplayTransition)
11971226

11981227
let toastHeight = self.toastNode.updateLayout(strings: self.presentationData.strings, content: self.toastContent, constrainedWidth: layout.size.width, bottomInset: layout.intrinsicInsets.bottom + buttonsHeight, transition: transition)
11991228

1229+
let toastSpacing: CGFloat = 22.0
1230+
let toastCollapsedOriginY = self.pictureInPictureTransitionFraction > 0.0 ? layout.size.height : layout.size.height - max(layout.intrinsicInsets.bottom, 20.0) - toastHeight
1231+
let toastOriginY = interpolate(from: toastCollapsedOriginY, to: defaultButtonsOriginY - toastSpacing - toastHeight, value: uiDisplayTransition)
1232+
12001233
var overlayAlpha: CGFloat = uiDisplayTransition
1234+
var toastAlpha: CGFloat = pipTransitionAlpha
12011235

12021236
switch self.callState?.state {
12031237
case .terminated, .terminating:
12041238
overlayAlpha *= 0.5
1239+
toastAlpha *= 0.5
12051240
default:
12061241
break
12071242
}
@@ -1229,15 +1264,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
12291264
apply()
12301265

12311266
let navigationOffset: CGFloat = max(20.0, layout.safeInsets.top)
1267+
let topOriginY = interpolate(from: -20.0, to: navigationOffset, value: uiDisplayTransition)
12321268

12331269
let backSize = self.backButtonNode.measure(CGSize(width: 320.0, height: 100.0))
12341270
if let image = self.backButtonArrowNode.image {
1235-
transition.updateFrame(node: self.backButtonArrowNode, frame: CGRect(origin: CGPoint(x: 10.0, y: navigationOffset + 11.0), size: image.size))
1271+
transition.updateFrame(node: self.backButtonArrowNode, frame: CGRect(origin: CGPoint(x: 10.0, y: topOriginY + 11.0), size: image.size))
12361272
}
1237-
transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: 29.0, y: navigationOffset + 11.0), size: backSize))
1273+
transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: 29.0, y: topOriginY + 11.0), size: backSize))
12381274

12391275
transition.updateAlpha(node: self.backButtonArrowNode, alpha: overlayAlpha)
12401276
transition.updateAlpha(node: self.backButtonNode, alpha: overlayAlpha)
1277+
transition.updateAlpha(node: self.toastNode, alpha: toastAlpha)
12411278

12421279
var statusOffset: CGFloat
12431280
if layout.metrics.widthClass == .regular && layout.metrics.heightClass == .regular {
@@ -1265,7 +1302,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
12651302
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: 100.0))
12661303
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))
12671304

1268-
transition.updateFrame(node: self.toastNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonsOriginY - toastHeight), size: CGSize(width: layout.size.width, height: toastHeight)))
1305+
transition.updateFrame(node: self.toastNode, frame: CGRect(origin: CGPoint(x: 0.0, y: toastOriginY), size: CGSize(width: layout.size.width, height: toastHeight)))
12691306
transition.updateFrame(node: self.buttonsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonsOriginY), size: CGSize(width: layout.size.width, height: buttonsHeight)))
12701307
transition.updateAlpha(node: self.buttonsNode, alpha: overlayAlpha)
12711308

@@ -1362,7 +1399,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
13621399
}
13631400

13641401
let keyTextSize = self.keyButtonNode.frame.size
1365-
transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: navigationOffset + 8.0), size: keyTextSize))
1402+
transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: topOriginY + 8.0), size: keyTextSize))
13661403
transition.updateAlpha(node: self.keyButtonNode, alpha: overlayAlpha)
13671404

13681405
if let debugNode = self.debugNode {
@@ -1719,7 +1756,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
17191756
case .collapsing:
17201757
self.pictureInPictureGestureState = .none
17211758
let velocity = recognizer.velocity(in: self.view).y
1722-
if abs(velocity) < 100.0 {
1759+
if abs(velocity) < 100.0 && self.pictureInPictureTransitionFraction < 0.5 {
17231760
if let (layout, navigationHeight) = self.validLayout {
17241761
self.pictureInPictureTransitionFraction = 0.0
17251762

0 commit comments

Comments
 (0)