Call controls confirmation toasts

This commit is contained in:
Marissa Le Coz 2024-01-03 14:46:40 -05:00 committed by GitHub
parent f89a56a43e
commit 320d7e2c72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 562 additions and 6 deletions

View File

@ -1581,6 +1581,7 @@
D9FC835029F71CC300878BC8 /* GroupUpdateInfoMessageInserter+PrecomputedUpdateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9FC834F29F71CC300878BC8 /* GroupUpdateInfoMessageInserter+PrecomputedUpdateType.swift */; };
E1368CBE18A1C36B00109378 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
E14EDF6E2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14EDF6D2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift */; };
E17283DE2B2A713C00302DC7 /* CallControlsConfirmationToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17283DD2B2A713C00302DC7 /* CallControlsConfirmationToast.swift */; };
E18C4A7729EF2ECC007534D4 /* SignalAccountTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18C4A7629EF2ECC007534D4 /* SignalAccountTest.swift */; };
E1A090382A4B909B00F2BE8B /* RecipientHidingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A090372A4B909B00F2BE8B /* RecipientHidingManager.swift */; };
E1C2A54B2A8FCB0D00AEC4DA /* DeleteSystemContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C2A54A2A8FCB0D00AEC4DA /* DeleteSystemContactViewController.swift */; };
@ -4313,6 +4314,7 @@
DE2DD605305BC6EFAD731723 /* Pods-Signal.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.debug.xcconfig"; sourceTree = "<group>"; };
DF728B4B438716EAF95CEC18 /* Pods-Signal.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.app store release.xcconfig"; sourceTree = "<group>"; };
E14EDF6D2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientContextMenuHelper.swift; sourceTree = "<group>"; };
E17283DD2B2A713C00302DC7 /* CallControlsConfirmationToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallControlsConfirmationToast.swift; sourceTree = "<group>"; };
E18C4A7629EF2ECC007534D4 /* SignalAccountTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalAccountTest.swift; sourceTree = "<group>"; };
E1A090372A4B909B00F2BE8B /* RecipientHidingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientHidingManager.swift; sourceTree = "<group>"; };
E1A0AD8B16E13FDD0071E604 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
@ -8062,6 +8064,7 @@
88588D27252E5A1F00405414 /* Individual */,
88238EA324E9DDE900F28079 /* CallButton.swift */,
88588D1A252D3DFF00405414 /* CallControls.swift */,
E17283DD2B2A713C00302DC7 /* CallControlsConfirmationToast.swift */,
88ABB8B42534070400229EAA /* CallHeader.swift */,
88D23D1C23CEC0C700B0E74B /* CallUIAdapter.swift */,
1700E33E28B856FB0073D949 /* IncomingCallControls.swift */,
@ -12029,6 +12032,7 @@
88D23D2C23CEC17400B0E74B /* CallAudioService.swift in Sources */,
88238EA424E9DDE900F28079 /* CallButton.swift in Sources */,
88588D1B252D3DFF00405414 /* CallControls.swift in Sources */,
E17283DE2B2A713C00302DC7 /* CallControlsConfirmationToast.swift in Sources */,
88ABB8B52534070400229EAA /* CallHeader.swift in Sources */,
88D23D2423CEC0C700B0E74B /* CallKitCallManager.swift in Sources */,
88D23D2523CEC0C700B0E74B /* CallKitCallUIAdaptee.swift in Sources */,

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "mic-slash.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,146 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 2.625000 10.791534 cm
0.000000 0.000000 0.000000 scn
4.869258 9.072965 m
6.282362 7.659862 l
6.501116 9.171856 7.802404 10.333466 9.375000 10.333466 c
11.100889 10.333466 12.500000 8.934355 12.500000 7.208466 c
12.500000 1.708466 l
12.500000 1.622302 12.496512 1.536952 12.489672 1.452551 c
13.942234 -0.000010 l
14.141206 0.531660 14.250000 1.107358 14.250000 1.708466 c
14.250000 7.208466 l
14.250000 9.900854 12.067389 12.083466 9.375000 12.083466 c
7.343011 12.083466 5.601388 10.840257 4.869258 9.072965 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.625000 16.983765 cm
0.000000 0.000000 0.000000 scn
10.303020 -9.270542 m
8.523958 -7.491481 l
7.482240 -7.197309 6.661456 -6.376525 6.367284 -5.334806 c
4.500000 -3.467522 l
4.500000 -4.483765 l
4.500000 -7.176152 6.682612 -9.358765 9.375000 -9.358765 c
9.692335 -9.358765 10.002587 -9.328444 10.303020 -9.270542 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.625000 9.625000 cm
0.000000 0.000000 0.000000 scn
11.715831 -3.324589 m
13.045951 -4.654709 l
12.186229 -5.074633 11.243936 -5.351652 10.250000 -5.454840 c
10.250000 -6.750000 l
13.625000 -6.750000 l
14.108249 -6.750000 14.500000 -7.141750 14.500000 -7.625000 c
14.500000 -8.108250 14.108249 -8.500000 13.625000 -8.500000 c
5.125000 -8.500000 l
4.641751 -8.500000 4.250000 -8.108250 4.250000 -7.625000 c
4.250000 -7.141750 4.641751 -6.750000 5.125000 -6.750000 c
8.500000 -6.750000 l
8.500000 -5.454840 l
4.285833 -5.017340 1.000000 -1.454895 1.000000 2.875000 c
1.000000 3.875000 l
1.000000 4.358249 1.391751 4.750000 1.875000 4.750000 c
2.358249 4.750000 2.750000 4.358249 2.750000 3.875000 c
2.750000 2.875000 l
2.750000 -0.783886 5.716114 -3.750000 9.375000 -3.750000 c
10.199077 -3.750000 10.988011 -3.599539 11.715831 -3.324589 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.625000 16.681549 cm
0.000000 0.000000 0.000000 scn
16.552219 -8.500011 m
15.266595 -7.214387 l
15.735333 -6.305653 16.000000 -5.274509 16.000000 -4.181549 c
16.000000 -3.181549 l
16.000000 -2.698300 16.391750 -2.306549 16.875000 -2.306549 c
17.358250 -2.306549 17.750000 -2.698300 17.750000 -3.181549 c
17.750000 -4.181549 l
17.750000 -5.761374 17.312569 -7.239032 16.552219 -8.500011 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.625000 3.954163 cm
0.000000 0.000000 0.000000 scn
1.493718 17.164557 m
1.152010 17.506264 0.597990 17.506264 0.256282 17.164557 c
-0.085427 16.822847 -0.085427 16.268827 0.256282 15.927119 c
17.256281 -1.072880 l
17.597990 -1.414589 18.152010 -1.414589 18.493717 -1.072880 c
18.835426 -0.731173 18.835426 -0.177153 18.493717 0.164557 c
1.493718 17.164557 l
h
f
n
Q
endstream
endobj
3 0 obj
2677
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002767 00000 n
0000002790 00000 n
0000002963 00000 n
0000003037 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
3096
%%EOF

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "speaker-slash.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,110 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 3.625000 2.954102 cm
0.000000 0.000000 0.000000 scn
1.493718 17.664618 m
1.152010 18.006325 0.597990 18.006325 0.256282 17.664618 c
-0.085427 17.322908 -0.085427 16.768888 0.256282 16.427179 c
16.256281 0.427179 l
16.597990 0.085470 17.152010 0.085470 17.493719 0.427179 c
17.835428 0.768888 17.835428 1.322906 17.493719 1.664616 c
1.493718 17.664618 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 3.625000 7.786987 cm
0.000000 0.000000 0.000000 scn
1.000000 6.213000 m
1.000000 6.915573 1.252011 7.559285 1.670569 8.058672 c
2.920504 6.808736 l
2.812453 6.635981 2.750000 6.431784 2.750000 6.213000 c
2.750000 2.213000 l
2.750000 1.591680 3.253680 1.088000 3.875000 1.088000 c
7.737508 1.088000 l
11.500001 -2.674492 l
11.500001 -1.770761 l
13.182144 -3.452904 l
12.850535 -4.569654 11.379924 -5.029287 10.475953 -4.125317 c
7.012634 -0.662000 l
3.875000 -0.662000 l
2.287181 -0.662000 1.000000 0.625181 1.000000 2.213000 c
1.000000 6.213000 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 3.625000 10.496582 cm
0.000000 0.000000 0.000000 scn
8.423059 5.314100 m
11.500000 8.391040 l
11.500000 2.237160 l
13.250001 0.487160 l
13.250000 8.692817 l
13.250000 10.140533 11.499649 10.865561 10.475953 9.841866 c
7.185623 6.551537 l
8.423059 5.314100 l
h
f
n
Q
endstream
endobj
3 0 obj
1327
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001417 00000 n
0000001440 00000 n
0000001613 00000 n
0000001687 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1746
%%EOF

View File

@ -102,9 +102,15 @@ class CallControls: UIView {
init(
call: SignalCall,
callService: CallService,
confirmationToastManager: CallControlsConfirmationToastManager,
delegate: CallControlsDelegate
) {
let viewModel = CallControlsViewModel(call: call, callService: callService, delegate: delegate)
let viewModel = CallControlsViewModel(
call: call,
callService: callService,
confirmationToastManager: confirmationToastManager,
delegate: delegate
)
self.viewModel = viewModel
self.delegate = delegate
super.init(frame: .zero)
@ -247,10 +253,17 @@ private class CallControlsViewModel {
private let call: SignalCall
private let callService: CallService
private weak var delegate: CallControlsDelegate?
private let confirmationToastManager: CallControlsConfirmationToastManager
fileprivate var refreshView: (() -> Void)?
init(call: SignalCall, callService: CallService, delegate: CallControlsDelegate) {
init(
call: SignalCall,
callService: CallService,
confirmationToastManager: CallControlsConfirmationToastManager,
delegate: CallControlsDelegate
) {
self.call = call
self.callService = callService
self.confirmationToastManager = confirmationToastManager
self.delegate = delegate
call.addObserverAndSyncState(observer: self)
callService.audioService.delegate = self
@ -541,14 +554,18 @@ extension CallControlsViewModel {
if callService.audioService.hasExternalInputs {
callService.audioService.presentRoutePicker()
} else {
callService.audioService.requestSpeakerphone(call: self.call, isEnabled: !audioSourceButtonIsSelected)
let shouldEnableSpeakerphone = !audioSourceButtonIsSelected
callService.audioService.requestSpeakerphone(call: self.call, isEnabled: shouldEnableSpeakerphone)
confirmationToastManager.toastInducingCallControlChangeDidOccur(state: .speakerphone(isOn: shouldEnableSpeakerphone))
}
refreshView?()
}
@objc
func didPressMute() {
callService.updateIsLocalAudioMuted(isLocalAudioMuted: !muteButtonIsSelected)
let shouldMute = !muteButtonIsSelected
callService.updateIsLocalAudioMuted(isLocalAudioMuted: shouldMute)
confirmationToastManager.toastInducingCallControlChangeDidOccur(state: .mute(isOn: shouldMute))
refreshView?()
}
@ -569,8 +586,10 @@ extension CallControlsViewModel {
switch call.groupCallRingState {
case .shouldRing:
call.groupCallRingState = .doNotRing
confirmationToastManager.toastInducingCallControlChangeDidOccur(state: .ring(isOn: false))
case .doNotRing:
call.groupCallRingState = .shouldRing
confirmationToastManager.toastInducingCallControlChangeDidOccur(state: .ring(isOn: true))
default:
owsFailBeta("Ring button should not have been available to press!")
}

View File

@ -0,0 +1,198 @@
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalMessaging
class CallControlsConfirmationToastView: UIView {
enum ControlType {
case mute(isOn: Bool)
case speakerphone(isOn: Bool)
case ring(isOn: Bool)
var imageName: String {
switch self {
case .mute(let isOn):
if isOn {
return "mic-slash"
} else {
return "mic"
}
case .speakerphone(let isOn):
if isOn {
return "speaker"
} else {
return "speaker-slash"
}
case .ring(let isOn):
if isOn {
return "bell"
} else {
return "bell-slash"
}
}
}
var text: String {
switch self {
case .mute(let isOn):
if isOn {
return OWSLocalizedString(
"MUTE_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that the mic has been muted for a call."
)
} else {
return OWSLocalizedString(
"UNMUTE_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that the mic has been unmuted for a call."
)
}
case .speakerphone(let isOn):
if isOn {
return OWSLocalizedString(
"SPEAKERPHONE_ON_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that the speakerphone has been turned on for a call."
)
} else {
return OWSLocalizedString(
"SPEAKERPHONE_OFF_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that the speakerphone has been turned off for a call."
)
}
case .ring(let isOn):
if isOn {
return OWSLocalizedString(
"RING_ON_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that ringing has been turned on for a call."
)
} else {
return OWSLocalizedString(
"RING_OFF_CONFIRMATION_TOAST_LABEL",
comment: "Text for a toast confirming that ringing has been turned off for a call."
)
}
}
}
}
private enum Style {
private static let opacity = UIAccessibility.isReduceTransparencyEnabled ? 0.8 : 0.6
static let toastBackgroundColor = UIColor(red: 0.29, green: 0.29, blue: 0.29, alpha: opacity)
static let textAndImageColor = UIColor(red: 0.91, green: 0.91, blue: 0.91, alpha: 1)
static let cornerRadius: CGFloat = 20
static let spacing: CGFloat = 8
static let horizontalMargin: CGFloat = 12
static let verticalMargin: CGFloat = 10
static let font: UIFont = .dynamicTypeBody2
static let imageDimension: CGFloat = 16
}
init(state: ControlType) {
super.init(frame: .zero)
// Image view
let imageView = UIImageView(image: UIImage(named: state.imageName))
imageView.tintColor = Style.textAndImageColor
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalToConstant: Style.imageDimension),
imageView.heightAnchor.constraint(equalToConstant: Style.imageDimension)
])
// Label
let label = UILabel()
label.textColor = Style.textAndImageColor
label.text = state.text
label.font = Style.font
// Stack
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = Style.spacing
stackView.alignment = .center
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(label)
stackView.translatesAutoresizingMaskIntoConstraints = false
// Blur view
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
blurView.translatesAutoresizingMaskIntoConstraints = false
// Container view
let containerView = UIView()
containerView.backgroundColor = Style.toastBackgroundColor
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(stackView)
NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: -Style.horizontalMargin),
containerView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: Style.horizontalMargin),
containerView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: Style.verticalMargin),
containerView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: -Style.verticalMargin),
])
// Self
self.addSubview(blurView)
self.addSubview(containerView)
self.layer.cornerRadius = Style.cornerRadius
self.clipsToBounds = true
blurView.autoPinEdgesToSuperviewEdges()
containerView.autoPinEdgesToSuperviewEdges()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class CallControlsConfirmationToastManager {
typealias ControlType = CallControlsConfirmationToastView.ControlType
private let presentingContainerView: UIView
init(presentingContainerView: UIView) {
self.presentingContainerView = presentingContainerView
}
func toastInducingCallControlChangeDidOccur(state: ControlType) {
self.presentToast(from: self.presentingContainerView, state: state)
}
private var toast: UIView?
private func presentToast(from view: UIView, state: ControlType) {
if let oldToast = self.toast {
// Handle case where new toast is triggered before old
// toast's disappearance animation completes.
oldToast.layer.removeAllAnimations()
oldToast.removeFromSuperview()
}
let toast = CallControlsConfirmationToastView(state: state)
self.toast = toast
toast.alpha = 0
view.addSubview(toast)
toast.transform = .scale(0.8)
toast.autoPinEdgesToSuperviewEdges()
let appearAnimator = UIViewPropertyAnimator(
duration: 0.2,
springDamping: 0.8,
springResponse: 0.2
)
appearAnimator.addAnimations {
toast.alpha = 1
toast.transform = .identity
}
appearAnimator.addCompletion { _ in
let disappearAnimator = UIViewPropertyAnimator(
duration: 0.2,
springDamping: 0.8,
springResponse: 0.2
)
disappearAnimator.addAnimations {
toast.alpha = 0
}
disappearAnimator.addCompletion { _ in
toast.removeFromSuperview()
}
disappearAnimator.startAnimation(afterDelay: 2)
}
appearAnimator.startAnimation()
}
}

View File

@ -14,7 +14,16 @@ import SignalUI
class GroupCallViewController: UIViewController {
private let call: SignalCall
private var groupCall: GroupCall { call.groupCall }
private lazy var callControls = CallControls(call: call, callService: callService, delegate: self)
private lazy var callControlsConfirmationToastManager = CallControlsConfirmationToastManager(
presentingContainerView: callControlsConfirmationToastContainerView
)
private lazy var callControls = CallControls(
call: call,
callService: callService,
confirmationToastManager: callControlsConfirmationToastManager,
delegate: self
)
private lazy var callControlsConfirmationToastContainerView = UIView()
private lazy var incomingCallControls = IncomingCallControls(video: true, delegate: self)
private var incomingCallControlsConstraint: NSLayoutConstraint?
private lazy var noVideoIndicatorView: UIStackView = createNoVideoIndicatorView()
@ -217,6 +226,15 @@ class GroupCallViewController: UIViewController {
swipeToastView.autoPinEdge(toSuperviewMargin: .leading, relation: .greaterThanOrEqual)
swipeToastView.autoPinEdge(toSuperviewMargin: .trailing, relation: .greaterThanOrEqual)
// Confirmation toasts should sit on top of the `localMemberView` and `videoOverflow`,
// so this `addSubview` should remain towards the end of the setup.
//
// TODO: The call controls '...' overflow menu (which will include reactions and raise hand
// options) will sit on top of these toasts.
view.addSubview(callControlsConfirmationToastContainerView)
callControlsConfirmationToastContainerView.autoPinEdge(.bottom, to: .top, of: callControls, withOffset: -30)
callControlsConfirmationToastContainerView.autoHCenterInSuperview()
view.addGestureRecognizer(tapGesture)
updateCallUI()
@ -382,7 +400,7 @@ class GroupCallViewController: UIViewController {
speakerPage.addSubview(speakerView)
speakerView.autoPinEdgesToSuperviewEdges()
view.addSubview(localMemberView)
view.insertSubview(localMemberView, belowSubview: callControlsConfirmationToastContainerView)
if groupCall.remoteDeviceStates.count > 1 {
let pipWidth = GroupCallVideoOverflow.itemHeight * ReturnToCallViewController.pipSize.aspectRatio

View File

@ -32,9 +32,14 @@ class IndividualCallViewController: OWSViewController, CallObserver {
private var callDurationTimer: Timer?
private lazy var callControlsConfirmationToastManager = CallControlsConfirmationToastManager(
presentingContainerView: callControlsConfirmationToastContainerView
)
private lazy var callControlsConfirmationToastContainerView = UIView()
private lazy var callControls = CallControls(
call: call,
callService: callService,
confirmationToastManager: callControlsConfirmationToastManager,
delegate: self
)
@ -275,6 +280,13 @@ class IndividualCallViewController: OWSViewController, CallObserver {
callControls.autoPinEdge(toSuperviewEdge: .leading)
callControls.autoPinEdge(toSuperviewEdge: .trailing)
// Confirmation toasts should sit on top of the `localVideoView`
// and most other UI elements, so this `addSubview` should remain towards
// the end of the setup.
view.addSubview(callControlsConfirmationToastContainerView)
callControlsConfirmationToastContainerView.autoPinEdge(.bottom, to: .top, of: callControls, withOffset: -30)
callControlsConfirmationToastContainerView.autoHCenterInSuperview()
createContactViews()
createIncomingCallControls()
}
@ -512,6 +524,7 @@ class IndividualCallViewController: OWSViewController, CallObserver {
guard !localVideoView.isHidden else { return }
view.bringSubviewToFront(localVideoView)
view.bringSubviewToFront(callControlsConfirmationToastContainerView)
let pipSize = ReturnToCallViewController.pipSize
let lastBoundingRect = lastLocalVideoBoundingRect

View File

@ -4192,6 +4192,9 @@
/* A recovery suggestion shown as the body of an alert when try to link a new device & the user is already at the limit. */
"MULTIDEVICE_PAIRING_MAX_RECOVERY" = "You have reached the maximum of devices you can currently link with your account. Please remove a device and try again.";
/* Text for a toast confirming that the mic has been muted for a call. */
"MUTE_CONFIRMATION_TOAST_LABEL" = "Mic off";
/* Badge indicating that the user is muted. */
"MUTED_BADGE" = "Muted";
@ -5689,6 +5692,12 @@
/* Button to return to current group call */
"RETURN_CALL_PILL_BUTTON" = "Return";
/* Text for a toast confirming that ringing has been turned off for a call. */
"RING_OFF_CONFIRMATION_TOAST_LABEL" = "Ringing off";
/* Text for a toast confirming that ringing has been turned on for a call. */
"RING_ON_CONFIRMATION_TOAST_LABEL" = "Ringing on";
/* button title to confirm adding a recipient when their safety number has recently changed */
"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_MEMBER_ACTION" = "Add Anyway";
@ -6937,6 +6946,12 @@
/* Title for the captcha view controller */
"SPAM_CAPTCHA_VIEW_CONTROLLER" = "Complete verification";
/* Text for a toast confirming that the speakerphone has been turned off for a call. */
"SPEAKERPHONE_OFF_CONFIRMATION_TOAST_LABEL" = "Speaker off";
/* Text for a toast confirming that the speakerphone has been turned on for a call. */
"SPEAKERPHONE_ON_CONFIRMATION_TOAST_LABEL" = "Speaker on";
/* The name for the sticker category 'Featured' */
"STICKER_CATEGORY_FEATURED_NAME" = "Featured";
@ -7588,6 +7603,9 @@
/* Alert title when unlinking device fails */
"UNLINKING_FAILED_ALERT_TITLE" = "Signal was unable to unlink your device.";
/* Text for a toast confirming that the mic has been unmuted for a call. */
"UNMUTE_CONFIRMATION_TOAST_LABEL" = "Mic on";
/* Pressing this button un-pins a thread */
"UNPIN_ACTION" = "Unpin";