diff --git a/Signal/src/Calls/UserInterface/CallControls.swift b/Signal/src/Calls/UserInterface/CallControls.swift index f3db7d7430..d1ad69eece 100644 --- a/Signal/src/Calls/UserInterface/CallControls.swift +++ b/Signal/src/Calls/UserInterface/CallControls.swift @@ -119,7 +119,8 @@ class CallControls: UIView { addSubview(controlsStack) controlsStack.autoPinWidthToSuperview() - controlsStack.autoPinEdge(toSuperviewSafeArea: .bottom, withInset: 56) + controlsStack.autoPinEdge(toSuperviewSafeArea: .bottom, withInset: 40, relation: .lessThanOrEqual) + controlsStack.autoPinEdge(toSuperviewSafeArea: .bottom, withInset: 56).priority = .defaultHigh - 1 controlsStack.autoPinEdge(toSuperviewEdge: .top) updateControls() diff --git a/Signal/src/Calls/UserInterface/Group/GroupCallMemberView.swift b/Signal/src/Calls/UserInterface/Group/GroupCallMemberView.swift index b4f126104b..6aa9d791cc 100644 --- a/Signal/src/Calls/UserInterface/Group/GroupCallMemberView.swift +++ b/Signal/src/Calls/UserInterface/Group/GroupCallMemberView.swift @@ -124,7 +124,6 @@ class GroupCallLocalMemberView: GroupCallMemberView { let videoView = LocalVideoView() let videoOffIndicatorImage = UIImageView() - let videoOffLabel = UILabel() var videoOffIndicatorWidth: CGFloat { if width > 102 { @@ -163,15 +162,6 @@ class GroupCallLocalMemberView: GroupCallMemberView { videoOffIndicatorImage.autoMatch(.height, to: .width, of: videoOffIndicatorImage) videoOffIndicatorImage.autoCenterInSuperview() - videoOffLabel.font = .ows_dynamicTypeSubheadline - videoOffLabel.text = NSLocalizedString("CALLING_MEMBER_VIEW_YOUR_CAMERA_IS_OFF", - comment: "Indicates to the user that their camera is currently off.") - videoOffLabel.textAlignment = .center - videoOffLabel.textColor = Theme.darkThemePrimaryColor - noVideoView.addSubview(videoOffLabel) - videoOffLabel.autoPinWidthToSuperview() - videoOffLabel.autoPinEdge(.top, to: .bottom, of: videoOffIndicatorImage, withOffset: 10) - videoView.contentMode = .scaleAspectFill insertSubview(videoView, belowSubview: muteIndicatorImage) videoView.frame = bounds @@ -193,8 +183,8 @@ class GroupCallLocalMemberView: GroupCallMemberView { videoView.captureSession = call.videoCaptureController.captureSession noVideoView.isHidden = !videoView.isHidden - videoOffLabel.isHidden = !videoView.isHidden || !isFullScreen - videoOffIndicatorImage.isHidden = !videoView.isHidden + // In full-screen mode the image is shown as part of the "Your camera is off" message. + videoOffIndicatorImage.isHidden = noVideoView.isHidden || isFullScreen guard let localAddress = tsAccountManager.localAddress else { return owsFailDebug("missing local address") diff --git a/Signal/src/Calls/UserInterface/Group/GroupCallViewController.swift b/Signal/src/Calls/UserInterface/Group/GroupCallViewController.swift index 21496617a8..9438d7978d 100644 --- a/Signal/src/Calls/UserInterface/Group/GroupCallViewController.swift +++ b/Signal/src/Calls/UserInterface/Group/GroupCallViewController.swift @@ -13,6 +13,7 @@ class GroupCallViewController: UIViewController { private let call: SignalCall private var groupCall: GroupCall { call.groupCall } private lazy var callControls = CallControls(call: call, delegate: self) + private lazy var noVideoIndicatorView: UIStackView = createNoVideoIndicatorView() private lazy var callHeader = CallHeader(call: call, delegate: self) private lazy var notificationView = GroupCallNotificationView(call: call) @@ -164,12 +165,19 @@ class GroupCallViewController: UIViewController { callHeader.autoPinWidthToSuperview() callHeader.autoPinEdge(toSuperviewEdge: .top) + view.addSubview(noVideoIndicatorView) + noVideoIndicatorView.autoHCenterInSuperview() + // Be flexible on the vertical centering on a cramped screen. + noVideoIndicatorView.autoVCenterInSuperview().priority = .defaultLow + noVideoIndicatorView.autoPinEdge(.top, to: .bottom, of: callHeader, withOffset: 8, relation: .greaterThanOrEqual) + view.addSubview(notificationView) notificationView.autoPinEdgesToSuperviewEdges() view.addSubview(callControls) callControls.autoPinWidthToSuperview() callControls.autoPinEdge(toSuperviewEdge: .bottom) + callControls.autoPinEdge(.top, to: .bottom, of: noVideoIndicatorView, withOffset: 8, relation: .greaterThanOrEqual) view.addSubview(videoOverflow) videoOverflow.autoPinEdge(toSuperviewEdge: .leading) @@ -188,6 +196,39 @@ class GroupCallViewController: UIViewController { updateCallUI() } + private func createNoVideoIndicatorView() -> UIStackView { + let icon = UIImageView() + icon.contentMode = .scaleAspectFit + icon.setTemplateImage(#imageLiteral(resourceName: "video-off-solid-28"), tintColor: .ows_white) + + let label = UILabel() + label.font = .ows_dynamicTypeCaption1 + label.text = NSLocalizedString("CALLING_MEMBER_VIEW_YOUR_CAMERA_IS_OFF", + comment: "Indicates to the user that their camera is currently off.") + label.textAlignment = .center + label.textColor = Theme.darkThemePrimaryColor + + let container = UIStackView(arrangedSubviews: [icon, label]) + if UIDevice.current.isIPhone5OrShorter { + // Use a horizontal layout to save on vertical space. + // Allow the icon to shrink below its natural size of 28pt... + icon.setContentCompressionResistancePriority(.defaultLow, for: .vertical) + container.axis = .horizontal + container.spacing = 4 + // ...by always matching the label's height. + container.alignment = .fill + } else { + // Use a simple vertical layout. + icon.autoSetDimensions(to: CGSize(square: 28)) + container.axis = .vertical + container.spacing = 10 + container.alignment = .center + label.autoPinWidthToSuperview() + } + + return container + } + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) @@ -411,6 +452,10 @@ class GroupCallViewController: UIViewController { guard !isCallMinimized else { return } + let showNoVideoIndicator = groupCall.remoteDeviceStates.isEmpty && groupCall.isOutgoingVideoMuted + // Hide the subviews of this view to collapse the stack. + noVideoIndicatorView.subviews.forEach { $0.isHidden = !showNoVideoIndicator } + let hideRemoteControls = shouldRemoteVideoControlsBeHidden && !groupCall.remoteDeviceStates.isEmpty let remoteControlsAreHidden = callControls.isHidden && callHeader.isHidden if hideRemoteControls != remoteControlsAreHidden {