Rip out a bunch of NonCallKitCallUIAdaptee
This commit is contained in:
parent
cab7fb76f4
commit
503d5c635e
@ -1394,7 +1394,6 @@
|
||||
886BB3D325BA0CA400079781 /* SetWallpaperViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ABAB8C25B8BE1E0008C78A /* SetWallpaperViewController.swift */; };
|
||||
886BB3D425BA0CA900079781 /* ColorAndWallpaperSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ABAB8A25B8B3CC0008C78A /* ColorAndWallpaperSettingsViewController.swift */; };
|
||||
886CB07824E77E5B00753909 /* silence.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 886CB07724E77E5B00753909 /* silence.aiff */; };
|
||||
886CB07C24E78F2200753909 /* Reflection.m4r in Resources */ = {isa = PBXBuildFile; fileRef = 886CB07B24E78F2200753909 /* Reflection.m4r */; };
|
||||
8871B16F25F007DE00D4E070 /* ProfileSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8871B16E25F007DE00D4E070 /* ProfileSettingsViewController.swift */; };
|
||||
8871B17125F0139D00D4E070 /* ProfileNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8871B17025F0139D00D4E070 /* ProfileNameViewController.swift */; };
|
||||
8876CE4A26A914B8002217ED /* SentMediaQualitySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8876CE4926A914B7002217ED /* SentMediaQualitySettingsViewController.swift */; };
|
||||
@ -1490,7 +1489,7 @@
|
||||
88D1D40422EF8A9700F472C5 /* ThreadDetailsInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D1D40322EF8A9700F472C5 /* ThreadDetailsInteraction.swift */; };
|
||||
88D23D1223CEBFB200B0E74B /* NotificationActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1123CEBFB200B0E74B /* NotificationActionHandler.swift */; };
|
||||
88D23D2023CEC0C700B0E74B /* IndividualCallService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1423CEC0C700B0E74B /* IndividualCallService.swift */; };
|
||||
88D23D2323CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1823CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift */; };
|
||||
88D23D2323CEC0C700B0E74B /* SimulatorCallUIAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1823CEC0C700B0E74B /* SimulatorCallUIAdaptee.swift */; };
|
||||
88D23D2423CEC0C700B0E74B /* CallKitCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1A23CEC0C700B0E74B /* CallKitCallManager.swift */; };
|
||||
88D23D2523CEC0C700B0E74B /* CallKitCallUIAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1B23CEC0C700B0E74B /* CallKitCallUIAdaptee.swift */; };
|
||||
88D23D2623CEC0C700B0E74B /* CallUIAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D23D1C23CEC0C700B0E74B /* CallUIAdapter.swift */; };
|
||||
@ -4215,7 +4214,6 @@
|
||||
886A58C8276A760600A1099B /* SubscriptionManagerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionManagerImpl.swift; sourceTree = "<group>"; };
|
||||
886A58C9276A760600A1099B /* ReceiptCredentialRedemptionJobQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiptCredentialRedemptionJobQueue.swift; sourceTree = "<group>"; };
|
||||
886CB07724E77E5B00753909 /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = "<group>"; };
|
||||
886CB07B24E78F2200753909 /* Reflection.m4r */ = {isa = PBXFileReference; lastKnownFileType = file; path = Reflection.m4r; sourceTree = "<group>"; };
|
||||
8871B16E25F007DE00D4E070 /* ProfileSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
8871B17025F0139D00D4E070 /* ProfileNameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNameViewController.swift; sourceTree = "<group>"; };
|
||||
8876CE4926A914B7002217ED /* SentMediaQualitySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentMediaQualitySettingsViewController.swift; sourceTree = "<group>"; };
|
||||
@ -4352,7 +4350,7 @@
|
||||
88D23D0D23CEBF6000B0E74B /* IndividualCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndividualCall.swift; sourceTree = "<group>"; };
|
||||
88D23D1123CEBFB200B0E74B /* NotificationActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationActionHandler.swift; sourceTree = "<group>"; };
|
||||
88D23D1423CEC0C700B0E74B /* IndividualCallService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndividualCallService.swift; sourceTree = "<group>"; };
|
||||
88D23D1823CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonCallKitCallUIAdaptee.swift; sourceTree = "<group>"; };
|
||||
88D23D1823CEC0C700B0E74B /* SimulatorCallUIAdaptee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimulatorCallUIAdaptee.swift; sourceTree = "<group>"; };
|
||||
88D23D1A23CEC0C700B0E74B /* CallKitCallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitCallManager.swift; sourceTree = "<group>"; };
|
||||
88D23D1B23CEC0C700B0E74B /* CallKitCallUIAdaptee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitCallUIAdaptee.swift; sourceTree = "<group>"; };
|
||||
88D23D1C23CEC0C700B0E74B /* CallUIAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallUIAdapter.swift; sourceTree = "<group>"; };
|
||||
@ -5798,7 +5796,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
34074FC5203E5435004596AE /* messageReceivedSounds */,
|
||||
34C3C78B20409F320000134C /* ringtoneSounds */,
|
||||
34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */,
|
||||
34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */,
|
||||
88D6E93E254CF712003142D9 /* group_call_join.aiff */,
|
||||
@ -6590,15 +6587,6 @@
|
||||
path = views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
34C3C78B20409F320000134C /* ringtoneSounds */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
886CB07B24E78F2200753909 /* Reflection.m4r */,
|
||||
);
|
||||
name = ringtoneSounds;
|
||||
path = Signal/AudioFiles/ringtoneSounds;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
34C6B0A41FA0E46F00D35993 /* Assets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -8894,11 +8882,11 @@
|
||||
34B3F83B1E8DF1700035BE1A /* IndividualCallViewController.swift */,
|
||||
88238EA124E9DDB700F28079 /* LocalVideoView.swift */,
|
||||
B935268F2B0E63C300421E6A /* NewCallViewController.swift */,
|
||||
88D23D1823CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift */,
|
||||
E1D827D42BD9B6E50022C1AF /* ReactionsBurstView.swift */,
|
||||
E1D827D62BD9DA4D0022C1AF /* ReactionsSink.swift */,
|
||||
766CE0D52A323DFE00AD609D /* RemoteVideoView.swift */,
|
||||
342FFE7D271EF5B1000AC89F /* ReturnToCallViewController.swift */,
|
||||
88D23D1823CEC0C700B0E74B /* SimulatorCallUIAdaptee.swift */,
|
||||
);
|
||||
path = UserInterface;
|
||||
sourceTree = "<group>";
|
||||
@ -11939,7 +11927,6 @@
|
||||
501052672BDB22940097DDC5 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
45B74A802044AAB600CD42F8 /* pulse-quiet.aifc in Resources */,
|
||||
45B74A822044AAB600CD42F8 /* pulse.aifc in Resources */,
|
||||
886CB07C24E78F2200753909 /* Reflection.m4r in Resources */,
|
||||
346EFC3C25FFDC6A00F493C7 /* restore-dark.json in Resources */,
|
||||
346EFC3B25FFDC6A00F493C7 /* restore.json in Resources */,
|
||||
34CF0788203E6B78005C4D61 /* ringback_tone_ansi.caf in Resources */,
|
||||
@ -13123,7 +13110,6 @@
|
||||
34995F1B2411838D00C70546 /* NewGroupMembersViewController.swift in Sources */,
|
||||
346594802434D49F00E5C510 /* NewGroupState.swift in Sources */,
|
||||
B9D65E532BAE1DA70067322A /* NicknameEditorViewController.swift in Sources */,
|
||||
88D23D2323CEC0C700B0E74B /* NonCallKitCallUIAdaptee.swift in Sources */,
|
||||
88D23D1223CEBFB200B0E74B /* NotificationActionHandler.swift in Sources */,
|
||||
8806EF19248DBD7200E764C7 /* NotificationPermissionReminderMegaphone.swift in Sources */,
|
||||
887B380D25F042BE00685845 /* NotificationSettingsContentViewController.swift in Sources */,
|
||||
@ -13266,6 +13252,7 @@
|
||||
880D90302481E617003D2B14 /* SignalApp.swift in Sources */,
|
||||
8841584C252F9F1C0078903D /* SignalCall.swift in Sources */,
|
||||
8822558D26B9D1D7001A33C4 /* SignalDotMePhoneNumberLink.swift in Sources */,
|
||||
88D23D2323CEC0C700B0E74B /* SimulatorCallUIAdaptee.swift in Sources */,
|
||||
88BE44A626153E7B00AE8E33 /* SoundAndNotificationsSettingsViewController.swift in Sources */,
|
||||
F94D12FF28BD0DD900B2C478 /* SpeechManager.swift in Sources */,
|
||||
34B3F87B1E8DF1700035BE1A /* SplashViewController.swift in Sources */,
|
||||
|
||||
Binary file not shown.
@ -16,9 +16,6 @@ protocol CallAudioServiceDelegate: AnyObject {
|
||||
|
||||
class CallAudioService: CallObserver {
|
||||
|
||||
private var vibrateTimer: Timer?
|
||||
|
||||
var handleRinging = false
|
||||
weak var delegate: CallAudioServiceDelegate? {
|
||||
willSet {
|
||||
assert(newValue == nil || delegate == nil)
|
||||
@ -28,13 +25,6 @@ class CallAudioService: CallObserver {
|
||||
// Track whether the speaker should be enabled or not.
|
||||
private(set) var isSpeakerEnabled = false
|
||||
|
||||
// MARK: Vibration config
|
||||
private let vibrateRepeatDuration = 1.6
|
||||
|
||||
// Our ring buzz is a pair of vibrations.
|
||||
// `pulseDuration` is the small pause between the two vibrations in the pair.
|
||||
private let pulseDuration = 0.2
|
||||
|
||||
private var observers = [NSObjectProtocol]()
|
||||
|
||||
private var avAudioSession: AVAudioSession {
|
||||
@ -278,8 +268,6 @@ class CallAudioService: CallObserver {
|
||||
private func handleLocalRinging(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
Logger.debug("")
|
||||
|
||||
startRinging(call: call)
|
||||
}
|
||||
|
||||
private func handleConnected(call: SignalCall) {
|
||||
@ -358,11 +346,10 @@ class CallAudioService: CallObserver {
|
||||
|
||||
// MARK: Playing Sounds
|
||||
|
||||
var currentPlayer: AudioPlayer?
|
||||
private var currentPlayer: AudioPlayer?
|
||||
|
||||
private func stopPlayingAnySounds() {
|
||||
currentPlayer?.stop()
|
||||
stopRinging()
|
||||
}
|
||||
|
||||
private func prepareToPlay(sound: StandardSound) -> AudioPlayer? {
|
||||
@ -386,63 +373,6 @@ class CallAudioService: CallObserver {
|
||||
newPlayer.play()
|
||||
}
|
||||
|
||||
// MARK: - Ringing
|
||||
|
||||
private var ringerSwitchObserver: CallRingerSwitchObserver?
|
||||
|
||||
func startRinging(call: SignalCall) {
|
||||
guard handleRinging else {
|
||||
Logger.debug("ignoring \(#function) since CallKit handles it's own ringing state")
|
||||
return
|
||||
}
|
||||
|
||||
// Only CallKit calls should be in the transitory ringing states
|
||||
switch call.mode {
|
||||
case .individual(let individualCall):
|
||||
owsAssertDebug(individualCall.state == .localRinging_ReadyToAnswer)
|
||||
case .groupThread:
|
||||
break
|
||||
}
|
||||
|
||||
vibrateTimer?.invalidate()
|
||||
vibrateTimer = .scheduledTimer(withTimeInterval: vibrateRepeatDuration, repeats: true) { [weak self] _ in
|
||||
self?.ringVibration()
|
||||
}
|
||||
|
||||
guard let player = prepareToPlay(sound: .defaultiOSIncomingRingtone) else {
|
||||
return owsFailDebug("Failed to prepare player for ringing")
|
||||
}
|
||||
|
||||
// Start observing (observes on init)
|
||||
ringerSwitchObserver = CallRingerSwitchObserver(callService: self, player: player, call: call)
|
||||
}
|
||||
|
||||
func stopRinging() {
|
||||
guard handleRinging else {
|
||||
Logger.debug("ignoring \(#function) since CallKit handles it's own ringing state")
|
||||
return
|
||||
}
|
||||
Logger.debug("")
|
||||
|
||||
// Stop vibrating
|
||||
vibrateTimer?.invalidate()
|
||||
vibrateTimer = nil
|
||||
|
||||
// Stops observing on deinit.
|
||||
ringerSwitchObserver = nil
|
||||
|
||||
currentPlayer?.stop()
|
||||
}
|
||||
|
||||
private func ringVibration() {
|
||||
// Since a call notification is more urgent than a message notification, we
|
||||
// vibrate twice, like a pulse, to differentiate from a normal notification vibration.
|
||||
vibrate()
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + pulseDuration) {
|
||||
self.vibrate()
|
||||
}
|
||||
}
|
||||
|
||||
func vibrate() {
|
||||
// TODO implement HapticAdapter for iPhone7 and up
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
|
||||
@ -559,38 +489,3 @@ extension CallAudioService: CallServiceStateObserver {
|
||||
newValue?.addObserverAndSyncState(observer: self)
|
||||
}
|
||||
}
|
||||
|
||||
private class CallRingerSwitchObserver: RingerSwitchObserver {
|
||||
|
||||
private let player: AudioPlayer
|
||||
private let call: SignalCall
|
||||
private weak var callService: CallAudioService?
|
||||
|
||||
init(callService: CallAudioService, player: AudioPlayer, call: SignalCall) {
|
||||
self.callService = callService
|
||||
self.player = player
|
||||
self.call = call
|
||||
|
||||
// Immediately callback to maintain old behavior.
|
||||
didToggleRingerSwitch(RingerSwitch.shared.addObserver(observer: self))
|
||||
}
|
||||
|
||||
deinit {
|
||||
RingerSwitch.shared.removeObserver(self)
|
||||
}
|
||||
|
||||
func didToggleRingerSwitch(_ isSilenced: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// We must ensure the proper audio session before
|
||||
// each time we play / pause, otherwise the category
|
||||
// may have changed and no playback would occur.
|
||||
callService?.ensureProperAudioSession(call: call)
|
||||
|
||||
if isSilenced {
|
||||
player.pause()
|
||||
} else {
|
||||
player.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,10 +187,6 @@ final class CallService: CallServiceStateObserver, CallServiceStateDelegate {
|
||||
break
|
||||
}
|
||||
|
||||
if let newValue {
|
||||
self.audioService.handleRinging = callUIAdapter.adaptee(for: newValue).hasManualRinger
|
||||
}
|
||||
|
||||
// To be safe, we reset the early ring on any call change so it's not left set from an unexpected state change.
|
||||
earlyRingNextIncomingCall = false
|
||||
}
|
||||
|
||||
@ -33,10 +33,6 @@ public enum CallState: String {
|
||||
case busyElsewhere // terminal
|
||||
}
|
||||
|
||||
public enum CallAdapterType {
|
||||
case `default`, nonCallKit
|
||||
}
|
||||
|
||||
public enum CallDirection {
|
||||
case outgoing, incoming
|
||||
}
|
||||
@ -87,8 +83,6 @@ public class IndividualCall: CustomDebugStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
let callAdapterType: CallAdapterType
|
||||
|
||||
weak var remoteVideoTrack: RTCVideoTrack? {
|
||||
didSet {
|
||||
AssertIsOnMainThread()
|
||||
@ -238,12 +232,11 @@ public class IndividualCall: CustomDebugStringConvertible {
|
||||
|
||||
// MARK: Initializers and Factory Methods
|
||||
|
||||
init(direction: CallDirection, state: CallState, thread: TSContactThread, sentAtTimestamp: UInt64, callAdapterType: CallAdapterType) {
|
||||
init(direction: CallDirection, state: CallState, thread: TSContactThread, sentAtTimestamp: UInt64) {
|
||||
self.direction = direction
|
||||
self.state = state
|
||||
self.thread = thread
|
||||
self.sentAtTimestamp = sentAtTimestamp
|
||||
self.callAdapterType = callAdapterType
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
@ -489,7 +489,6 @@ final class IndividualCallService: CallServiceStateObserver {
|
||||
if !isOutgoing {
|
||||
// If we are using the NSE, we need to kick off a ring ASAP in case this incoming call
|
||||
// has resulted in the NSE waking up the main app.
|
||||
owsAssertDebug(callUIAdapter.adaptee(for: call) === callUIAdapter.callKitAdaptee)
|
||||
Logger.info("Performing early ring")
|
||||
handleRinging(call: call, isAnticipatory: true)
|
||||
} else {
|
||||
|
||||
@ -313,8 +313,7 @@ class SignalCall: CallManagerCallReference {
|
||||
direction: .outgoing,
|
||||
state: .dialing,
|
||||
thread: thread,
|
||||
sentAtTimestamp: Date.ows_millisecondTimestamp(),
|
||||
callAdapterType: .default
|
||||
sentAtTimestamp: Date.ows_millisecondTimestamp()
|
||||
)
|
||||
}
|
||||
|
||||
@ -323,13 +322,11 @@ class SignalCall: CallManagerCallReference {
|
||||
sentAtTimestamp: UInt64,
|
||||
offerMediaType: TSRecentCallOfferType
|
||||
) -> SignalCall {
|
||||
let callAdapterType: CallAdapterType = .default
|
||||
let individualCall = IndividualCall(
|
||||
direction: .incoming,
|
||||
state: .answering,
|
||||
thread: thread,
|
||||
sentAtTimestamp: sentAtTimestamp,
|
||||
callAdapterType: callAdapterType
|
||||
sentAtTimestamp: sentAtTimestamp
|
||||
)
|
||||
individualCall.offerMediaType = offerMediaType
|
||||
return SignalCall(individualCall: individualCall)
|
||||
|
||||
@ -22,9 +22,6 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
|
||||
private let provider: CXProvider
|
||||
private let audioActivity: AudioActivity
|
||||
|
||||
// CallKit handles incoming ringer stop/start for us. Yay!
|
||||
let hasManualRinger = false
|
||||
|
||||
// Instantiating more than one CXProvider can cause us to miss call transactions, so
|
||||
// we maintain the provider across Adaptees using a singleton pattern
|
||||
static private let providerReadyFlag: ReadyFlag = ReadyFlag(name: "CallKitCXProviderReady")
|
||||
@ -129,9 +126,6 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
|
||||
AssertIsOnMainThread()
|
||||
Logger.info("")
|
||||
|
||||
// make sure we don't terminate audio session during call
|
||||
_ = self.audioSession.startAudioActivity(call.audioActivity)
|
||||
|
||||
// Add the new outgoing call to the app's list of calls.
|
||||
// So we can find it in the provider delegate callbacks.
|
||||
Self.providerReadyFlag.runNowOrWhenDidBecomeReadySync {
|
||||
@ -219,19 +213,11 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
|
||||
|
||||
completion(nil)
|
||||
|
||||
self.showCall(call)
|
||||
self.callManager.addCall(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func answerCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
Logger.info("")
|
||||
|
||||
owsFailDebug("CallKit should answer calls via system call screen, not via notifications.")
|
||||
}
|
||||
|
||||
func answerCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
Logger.info("")
|
||||
@ -265,12 +251,6 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func localHangupCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
owsFailDebug("CallKit should decline calls via system call screen, not via notifications.")
|
||||
}
|
||||
|
||||
func localHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
Logger.info("")
|
||||
|
||||
@ -12,15 +12,13 @@ import UIKit
|
||||
protocol CallUIAdaptee: AnyObject {
|
||||
var notificationPresenterImpl: NotificationPresenterImpl { get }
|
||||
var callService: CallService { get }
|
||||
var hasManualRinger: Bool { get }
|
||||
|
||||
init(showNamesOnCallScreen: Bool, useSystemCallLog: Bool)
|
||||
|
||||
func startOutgoingCall(call: SignalCall)
|
||||
func reportIncomingCall(_ call: SignalCall, completion: @escaping (Error?) -> Void)
|
||||
func reportMissedCall(_ call: SignalCall, individualCall: IndividualCall)
|
||||
func answerCall(localId: UUID)
|
||||
func answerCall(_ call: SignalCall)
|
||||
func recipientAcceptedCall(_ call: SignalCall)
|
||||
func localHangupCall(localId: UUID)
|
||||
func localHangupCall(_ call: SignalCall)
|
||||
func remoteDidHangupCall(_ call: SignalCall)
|
||||
func remoteBusy(_ call: SignalCall)
|
||||
@ -30,65 +28,6 @@ protocol CallUIAdaptee: AnyObject {
|
||||
func failCall(_ call: SignalCall, error: SignalCall.CallError)
|
||||
func setIsMuted(call: SignalCall, isMuted: Bool)
|
||||
func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool)
|
||||
func startAndShowOutgoingCall(thread: TSContactThread, hasLocalVideo: Bool)
|
||||
}
|
||||
|
||||
// Shared default implementations
|
||||
extension CallUIAdaptee {
|
||||
|
||||
internal func showCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard !call.hasTerminated else {
|
||||
Logger.info("Not showing window for terminated call \(call)")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.info("\(call)")
|
||||
|
||||
let callViewController: UIViewController & CallViewControllerWindowReference
|
||||
switch call.mode {
|
||||
case .individual(let individualCall):
|
||||
callViewController = IndividualCallViewController(call: call, individualCall: individualCall)
|
||||
case .groupThread(let groupThreadCall):
|
||||
callViewController = GroupCallViewController(call: call, groupCall: groupThreadCall.ringRtcCall)
|
||||
}
|
||||
|
||||
callViewController.modalTransitionStyle = .crossDissolve
|
||||
WindowManager.shared.startCall(viewController: callViewController)
|
||||
}
|
||||
|
||||
internal func reportMissedCall(_ call: SignalCall, individualCall: IndividualCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
let sentAtTimestamp = Date(millisecondsSince1970: individualCall.sentAtTimestamp)
|
||||
notificationPresenterImpl.presentMissedCall(
|
||||
call,
|
||||
caller: individualCall.remoteAddress,
|
||||
sentAt: sentAtTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
internal func startAndShowOutgoingCall(thread: TSContactThread, hasLocalVideo: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let (call, individualCall) = self.callService.buildOutgoingIndividualCallIfPossible(
|
||||
thread: thread,
|
||||
hasVideo: hasLocalVideo
|
||||
) else {
|
||||
// @integration This is not unexpected, it could happen if Bob tries
|
||||
// to start an outgoing call at the same moment Alice has already
|
||||
// sent him an Offer that is being processed.
|
||||
Logger.info("found an existing call when trying to start outgoing call: \(thread.contactAddress)")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.debug("")
|
||||
|
||||
startOutgoingCall(call: call)
|
||||
individualCall.hasLocalVideo = hasLocalVideo
|
||||
self.showCall(call)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,42 +38,25 @@ public class CallUIAdapter: NSObject {
|
||||
|
||||
private var callService: CallService { AppEnvironment.shared.callService }
|
||||
|
||||
lazy var nonCallKitAdaptee = NonCallKitCallUIAdaptee()
|
||||
|
||||
lazy var callKitAdaptee: CallKitCallUIAdaptee? = { () -> CallKitCallUIAdaptee? in
|
||||
if Platform.isSimulator {
|
||||
// CallKit doesn't seem entirely supported in simulator.
|
||||
// e.g. you can't receive calls in the call screen.
|
||||
// So we use the non-CallKit call UI.
|
||||
Logger.info("not using callkit adaptee for simulator.")
|
||||
return nil
|
||||
} else {
|
||||
Logger.info("using callkit adaptee for iOS11+")
|
||||
let (showNames, useSystemCallLog) = databaseStorage.read { tx in
|
||||
return (
|
||||
preferences.notificationPreviewType(tx: tx) != .noNameNoPreview,
|
||||
preferences.isSystemCallLogEnabled(tx: tx)
|
||||
)
|
||||
}
|
||||
|
||||
return CallKitCallUIAdaptee(
|
||||
showNamesOnCallScreen: showNames,
|
||||
useSystemCallLog: useSystemCallLog
|
||||
private lazy var adaptee: any CallUIAdaptee = { () -> any CallUIAdaptee in
|
||||
let callUIAdapteeType: CallUIAdaptee.Type
|
||||
#if targetEnvironment(simulator)
|
||||
callUIAdapteeType = SimulatorCallUIAdaptee.self
|
||||
#else
|
||||
callUIAdapteeType = CallKitCallUIAdaptee.self
|
||||
#endif
|
||||
let (showNames, useSystemCallLog) = databaseStorage.read { tx in
|
||||
return (
|
||||
preferences.notificationPreviewType(tx: tx) != .noNameNoPreview,
|
||||
preferences.isSystemCallLogEnabled(tx: tx)
|
||||
)
|
||||
}
|
||||
return callUIAdapteeType.init(
|
||||
showNamesOnCallScreen: showNames,
|
||||
useSystemCallLog: useSystemCallLog
|
||||
)
|
||||
}()
|
||||
|
||||
var defaultAdaptee: CallUIAdaptee { callKitAdaptee ?? nonCallKitAdaptee }
|
||||
|
||||
func adaptee(for call: SignalCall) -> CallUIAdaptee {
|
||||
switch call.mode {
|
||||
case .individual(let individualCall) where individualCall.callAdapterType == .nonCallKit:
|
||||
return nonCallKitAdaptee
|
||||
case .individual, .groupThread:
|
||||
return defaultAdaptee
|
||||
}
|
||||
}
|
||||
|
||||
public override init() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
@ -156,24 +78,11 @@ public class CallUIAdapter: NSObject {
|
||||
// make sure we don't terminate audio session during call
|
||||
_ = audioSession.startAudioActivity(call.audioActivity)
|
||||
|
||||
adaptee(for: call).reportIncomingCall(call) { error in
|
||||
adaptee.reportIncomingCall(call) { error in
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard var error = error else {
|
||||
switch call.mode {
|
||||
case .individual:
|
||||
// Individual calls ring on their state transitions, but group calls ring immediately.
|
||||
break
|
||||
case .groupThread:
|
||||
// Wait to start ringing until all observers have recognized this as the current call.
|
||||
DispatchQueue.main.async {
|
||||
guard call === self.callService.callServiceState.currentCall else {
|
||||
// Assume that the call failed before we got a chance to start ringing.
|
||||
return
|
||||
}
|
||||
self.callService.audioService.startRinging(call: call)
|
||||
}
|
||||
}
|
||||
self.showCall(call)
|
||||
return
|
||||
}
|
||||
|
||||
@ -205,118 +114,125 @@ public class CallUIAdapter: NSObject {
|
||||
internal func reportMissedCall(_ call: SignalCall, individualCall: IndividualCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).reportMissedCall(call, individualCall: individualCall)
|
||||
let sentAtTimestamp = Date(millisecondsSince1970: individualCall.sentAtTimestamp)
|
||||
notificationPresenterImpl.presentMissedCall(
|
||||
call,
|
||||
caller: individualCall.remoteAddress,
|
||||
sentAt: sentAtTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
internal func startOutgoingCall(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).startOutgoingCall(call: call)
|
||||
}
|
||||
// make sure we don't terminate audio session during call
|
||||
_ = audioSession.startAudioActivity(call.audioActivity)
|
||||
|
||||
public func answerCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let call = self.callService.callServiceState.currentCall else {
|
||||
owsFailDebug("No current call.")
|
||||
return
|
||||
}
|
||||
|
||||
guard call.localId == localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
adaptee(for: call).answerCall(localId: localId)
|
||||
adaptee.startOutgoingCall(call: call)
|
||||
}
|
||||
|
||||
internal func answerCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).answerCall(call)
|
||||
adaptee.answerCall(call)
|
||||
}
|
||||
|
||||
public func startAndShowOutgoingCall(thread: TSContactThread, hasLocalVideo: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
defaultAdaptee.startAndShowOutgoingCall(thread: thread, hasLocalVideo: hasLocalVideo)
|
||||
guard let (call, individualCall) = self.callService.buildOutgoingIndividualCallIfPossible(
|
||||
thread: thread,
|
||||
hasVideo: hasLocalVideo
|
||||
) else {
|
||||
// @integration This is not unexpected, it could happen if Bob tries
|
||||
// to start an outgoing call at the same moment Alice has already
|
||||
// sent him an Offer that is being processed.
|
||||
Logger.info("found an existing call when trying to start outgoing call: \(thread.contactAddress)")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.debug("")
|
||||
|
||||
startOutgoingCall(call: call)
|
||||
individualCall.hasLocalVideo = hasLocalVideo
|
||||
self.showCall(call)
|
||||
}
|
||||
|
||||
internal func recipientAcceptedCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).recipientAcceptedCall(call)
|
||||
adaptee.recipientAcceptedCall(call)
|
||||
}
|
||||
|
||||
internal func remoteDidHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).remoteDidHangupCall(call)
|
||||
adaptee.remoteDidHangupCall(call)
|
||||
}
|
||||
|
||||
internal func remoteBusy(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).remoteBusy(call)
|
||||
adaptee.remoteBusy(call)
|
||||
}
|
||||
|
||||
internal func didAnswerElsewhere(call: SignalCall) {
|
||||
adaptee(for: call).didAnswerElsewhere(call: call)
|
||||
adaptee.didAnswerElsewhere(call: call)
|
||||
}
|
||||
|
||||
internal func didDeclineElsewhere(call: SignalCall) {
|
||||
adaptee(for: call).didDeclineElsewhere(call: call)
|
||||
adaptee.didDeclineElsewhere(call: call)
|
||||
}
|
||||
|
||||
internal func wasBusyElsewhere(call: SignalCall) {
|
||||
adaptee(for: call).wasBusyElsewhere(call: call)
|
||||
}
|
||||
|
||||
internal func localHangupCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let call = self.callService.callServiceState.currentCall else {
|
||||
owsFailDebug("No current call.")
|
||||
return
|
||||
}
|
||||
|
||||
guard call.localId == localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
adaptee(for: call).localHangupCall(localId: localId)
|
||||
adaptee.wasBusyElsewhere(call: call)
|
||||
}
|
||||
|
||||
internal func localHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).localHangupCall(call)
|
||||
adaptee.localHangupCall(call)
|
||||
}
|
||||
|
||||
internal func failCall(_ call: SignalCall, error: SignalCall.CallError) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).failCall(call, error: error)
|
||||
adaptee.failCall(call, error: error)
|
||||
}
|
||||
|
||||
private func showCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).showCall(call)
|
||||
guard !call.hasTerminated else {
|
||||
Logger.info("Not showing window for terminated call \(call)")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.info("\(call)")
|
||||
|
||||
let callViewController: UIViewController & CallViewControllerWindowReference
|
||||
switch call.mode {
|
||||
case .individual(let individualCall):
|
||||
callViewController = IndividualCallViewController(call: call, individualCall: individualCall)
|
||||
case .groupThread(let groupThreadCall):
|
||||
callViewController = GroupCallViewController(call: call, groupCall: groupThreadCall.ringRtcCall)
|
||||
}
|
||||
|
||||
callViewController.modalTransitionStyle = .crossDissolve
|
||||
WindowManager.shared.startCall(viewController: callViewController)
|
||||
}
|
||||
|
||||
internal func setIsMuted(call: SignalCall, isMuted: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// With CallKit, muting is handled by a CXAction, so it must go through the adaptee
|
||||
adaptee(for: call).setIsMuted(call: call, isMuted: isMuted)
|
||||
adaptee.setIsMuted(call: call, isMuted: isMuted)
|
||||
}
|
||||
|
||||
internal func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
adaptee(for: call).setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo)
|
||||
adaptee.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo)
|
||||
}
|
||||
|
||||
internal func setCameraSource(call: SignalCall, isUsingFrontCamera: Bool) {
|
||||
|
||||
@ -1,239 +0,0 @@
|
||||
//
|
||||
// Copyright 2016 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalServiceKit
|
||||
|
||||
/**
|
||||
* Manage call related UI in a pre-CallKit world.
|
||||
*/
|
||||
class NonCallKitCallUIAdaptee: NSObject, CallUIAdaptee {
|
||||
var callService: CallService { AppEnvironment.shared.callService }
|
||||
|
||||
// Starting/Stopping incoming call ringing is our apps responsibility for the non CallKit interface.
|
||||
let hasManualRinger = true
|
||||
|
||||
override init() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
func startOutgoingCall(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// make sure we don't terminate audio session during call
|
||||
let success = self.audioSession.startAudioActivity(call.audioActivity)
|
||||
assert(success)
|
||||
|
||||
switch call.mode {
|
||||
case .individual:
|
||||
self.callService.individualCallService.handleOutgoingCall(call)
|
||||
case .groupThread:
|
||||
switch call.groupCallRingState {
|
||||
case .shouldRing where call.ringRestrictions.isEmpty, .ringing:
|
||||
// Let CallService call recipientAcceptedCall when someone joins.
|
||||
break
|
||||
case .ringingEnded:
|
||||
owsFailDebug("ringing ended while we were starting the call")
|
||||
fallthrough
|
||||
case .doNotRing, .shouldRing:
|
||||
// Immediately consider ourselves connected.
|
||||
recipientAcceptedCall(call)
|
||||
case .incomingRing, .incomingRingCancelled:
|
||||
owsFailDebug("should not happen for an outgoing call")
|
||||
// Recover by considering ourselves connected
|
||||
recipientAcceptedCall(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reportIncomingCall(_ call: SignalCall, completion: @escaping (Error?) -> Void) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("")
|
||||
|
||||
self.showCall(call)
|
||||
|
||||
startNotifiyingForIncomingCall(call)
|
||||
|
||||
completion(nil)
|
||||
}
|
||||
|
||||
private var incomingCallNotificationTimer: Timer?
|
||||
private func startNotifiyingForIncomingCall(_ call: SignalCall) {
|
||||
// Pull this out up front. Group calls don't necessarily keep the information around.
|
||||
guard let caller = call.caller else {
|
||||
return
|
||||
}
|
||||
|
||||
incomingCallNotificationTimer?.invalidate()
|
||||
|
||||
// present lock screen notification if we're in the background.
|
||||
// we re-present the notifiation every 3 seconds to make sure
|
||||
// the user sees that their phone is ringing
|
||||
incomingCallNotificationTimer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [weak self] timer in
|
||||
guard let self = self else {
|
||||
timer.invalidate()
|
||||
return
|
||||
}
|
||||
|
||||
let shouldContinue = { () -> Bool in
|
||||
guard self.callService.callServiceState.currentCall === call else {
|
||||
return false
|
||||
}
|
||||
switch call.mode {
|
||||
case .individual(let individualCall):
|
||||
return individualCall.state == .localRinging_ReadyToAnswer
|
||||
case .groupThread(let groupThreadCall):
|
||||
return groupThreadCall.ringRtcCall.localDeviceState.joinState == .notJoined
|
||||
}
|
||||
}()
|
||||
guard shouldContinue else {
|
||||
self.incomingCallNotificationTimer?.invalidate()
|
||||
self.incomingCallNotificationTimer = nil
|
||||
return
|
||||
}
|
||||
if UIApplication.shared.applicationState == .active {
|
||||
Logger.debug("skipping notification since app is already active.")
|
||||
} else {
|
||||
self.notificationPresenterImpl.presentIncomingCall(call, caller: caller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func answerCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let call = self.callService.callServiceState.currentCall else {
|
||||
owsFailDebug("No current call.")
|
||||
return
|
||||
}
|
||||
|
||||
guard call.localId == localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
self.answerCall(call)
|
||||
}
|
||||
|
||||
func answerCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard call.localId == self.callService.callServiceState.currentCall?.localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
switch call.mode {
|
||||
case .individual:
|
||||
self.callService.individualCallService.handleAcceptCall(call)
|
||||
case .groupThread(let groupThreadCall):
|
||||
// Explicitly unmute to request permissions.
|
||||
self.callService.updateIsLocalAudioMuted(isLocalAudioMuted: false)
|
||||
self.callService.joinGroupCallIfNecessary(call, groupCall: groupThreadCall.ringRtcCall)
|
||||
}
|
||||
|
||||
// Enable audio for locally accepted calls after the session is configured.
|
||||
self.audioSession.isRTCAudioEnabled = true
|
||||
}
|
||||
|
||||
func recipientAcceptedCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Enable audio for remotely accepted calls after the session is configured.
|
||||
self.audioSession.isRTCAudioEnabled = true
|
||||
}
|
||||
|
||||
func localHangupCall(localId: UUID) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let call = self.callService.callServiceState.currentCall else {
|
||||
owsFailDebug("No current call.")
|
||||
return
|
||||
}
|
||||
|
||||
guard call.localId == localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
self.localHangupCall(call)
|
||||
}
|
||||
|
||||
func localHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// If both parties hang up at the same moment,
|
||||
// call might already be nil.
|
||||
guard self.callService.callServiceState.currentCall == nil || call.localId == self.callService.callServiceState.currentCall?.localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
callService.handleLocalHangupCall(call)
|
||||
}
|
||||
|
||||
internal func remoteDidHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
internal func remoteBusy(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
internal func didAnswerElsewhere(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
internal func didDeclineElsewhere(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
internal func wasBusyElsewhere(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
internal func failCall(_ call: SignalCall, error: SignalCall.CallError) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
Logger.debug("is no-op")
|
||||
}
|
||||
|
||||
func setIsMuted(call: SignalCall, isMuted: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard call.localId == self.callService.callServiceState.currentCall?.localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
self.callService.updateIsLocalAudioMuted(isLocalAudioMuted: isMuted)
|
||||
}
|
||||
|
||||
func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard call.localId == self.callService.callServiceState.currentCall?.localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
self.callService.updateIsLocalVideoMuted(isLocalVideoMuted: !hasLocalVideo)
|
||||
}
|
||||
}
|
||||
115
Signal/Calls/UserInterface/SimulatorCallUIAdaptee.swift
Normal file
115
Signal/Calls/UserInterface/SimulatorCallUIAdaptee.swift
Normal file
@ -0,0 +1,115 @@
|
||||
//
|
||||
// Copyright 2016 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalServiceKit
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
|
||||
class SimulatorCallUIAdaptee: NSObject, CallUIAdaptee {
|
||||
var callService: CallService { AppEnvironment.shared.callService }
|
||||
|
||||
required init(showNamesOnCallScreen: Bool, useSystemCallLog: Bool) {
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
func startOutgoingCall(call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
switch call.mode {
|
||||
case .individual:
|
||||
self.callService.individualCallService.handleOutgoingCall(call)
|
||||
case .groupThread:
|
||||
switch call.groupCallRingState {
|
||||
case .shouldRing where call.ringRestrictions.isEmpty, .ringing:
|
||||
// Let CallService call recipientAcceptedCall when someone joins.
|
||||
break
|
||||
case .ringingEnded:
|
||||
owsFailDebug("ringing ended while we were starting the call")
|
||||
fallthrough
|
||||
case .doNotRing, .shouldRing:
|
||||
// Immediately consider ourselves connected.
|
||||
recipientAcceptedCall(call)
|
||||
case .incomingRing, .incomingRingCancelled:
|
||||
owsFailDebug("should not happen for an outgoing call")
|
||||
// Recover by considering ourselves connected
|
||||
recipientAcceptedCall(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reportIncomingCall(_ call: SignalCall, completion: @escaping (Error?) -> Void) {
|
||||
AssertIsOnMainThread()
|
||||
completion(nil)
|
||||
}
|
||||
|
||||
func answerCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard call.localId == self.callService.callServiceState.currentCall?.localId else {
|
||||
owsFailDebug("localId does not match current call")
|
||||
return
|
||||
}
|
||||
|
||||
switch call.mode {
|
||||
case .individual:
|
||||
self.callService.individualCallService.handleAcceptCall(call)
|
||||
case .groupThread(let groupThreadCall):
|
||||
// Explicitly unmute to request permissions.
|
||||
self.callService.updateIsLocalAudioMuted(isLocalAudioMuted: false)
|
||||
self.callService.joinGroupCallIfNecessary(call, groupCall: groupThreadCall.ringRtcCall)
|
||||
}
|
||||
|
||||
// Enable audio for locally accepted calls after the session is configured.
|
||||
self.audioSession.isRTCAudioEnabled = true
|
||||
}
|
||||
|
||||
func recipientAcceptedCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// Enable audio for remotely accepted calls after the session is configured.
|
||||
self.audioSession.isRTCAudioEnabled = true
|
||||
}
|
||||
|
||||
func localHangupCall(_ call: SignalCall) {
|
||||
AssertIsOnMainThread()
|
||||
// If both parties hang up at the same moment, call might already be nil.
|
||||
owsAssert(self.callService.callServiceState.currentCall == nil || call.localId == self.callService.callServiceState.currentCall?.localId)
|
||||
callService.handleLocalHangupCall(call)
|
||||
}
|
||||
|
||||
func remoteDidHangupCall(_ call: SignalCall) {
|
||||
}
|
||||
|
||||
func remoteBusy(_ call: SignalCall) {
|
||||
}
|
||||
|
||||
func didAnswerElsewhere(call: SignalCall) {
|
||||
}
|
||||
|
||||
func didDeclineElsewhere(call: SignalCall) {
|
||||
}
|
||||
|
||||
func wasBusyElsewhere(call: SignalCall) {
|
||||
}
|
||||
|
||||
func failCall(_ call: SignalCall, error: SignalCall.CallError) {
|
||||
}
|
||||
|
||||
func setIsMuted(call: SignalCall, isMuted: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
owsAssert(call.localId == self.callService.callServiceState.currentCall?.localId)
|
||||
self.callService.updateIsLocalAudioMuted(isLocalAudioMuted: isMuted)
|
||||
}
|
||||
|
||||
func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
|
||||
AssertIsOnMainThread()
|
||||
owsAssert(call.localId == self.callService.callServiceState.currentCall?.localId)
|
||||
self.callService.updateIsLocalVideoMuted(isLocalVideoMuted: !hasLocalVideo)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -53,12 +53,8 @@ public class NotificationActionHandler: Dependencies {
|
||||
}
|
||||
|
||||
switch action {
|
||||
case .answerCall:
|
||||
return try answerCall(userInfo: userInfo)
|
||||
case .callBack:
|
||||
return try callBack(userInfo: userInfo)
|
||||
case .declineCall:
|
||||
return try declineCall(userInfo: userInfo)
|
||||
case .markAsRead:
|
||||
return try markAsRead(userInfo: userInfo)
|
||||
case .reply:
|
||||
@ -87,19 +83,6 @@ public class NotificationActionHandler: Dependencies {
|
||||
|
||||
// MARK: -
|
||||
|
||||
private class func answerCall(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
|
||||
guard let localCallIdString = userInfo[AppNotificationUserInfoKey.localCallId] as? String else {
|
||||
throw OWSAssertionError("localCallIdString was unexpectedly nil")
|
||||
}
|
||||
|
||||
guard let localCallId = UUID(uuidString: localCallIdString) else {
|
||||
throw OWSAssertionError("unable to build localCallId. localCallIdString: \(localCallIdString)")
|
||||
}
|
||||
|
||||
callService.callUIAdapter.answerCall(localId: localCallId)
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
private class func callBack(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
|
||||
let aciString = userInfo[AppNotificationUserInfoKey.callBackAciString] as? String
|
||||
let phoneNumber = userInfo[AppNotificationUserInfoKey.callBackPhoneNumber] as? String
|
||||
@ -113,19 +96,6 @@ public class NotificationActionHandler: Dependencies {
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
private class func declineCall(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
|
||||
guard let localCallIdString = userInfo[AppNotificationUserInfoKey.localCallId] as? String else {
|
||||
throw OWSAssertionError("localCallIdString was unexpectedly nil")
|
||||
}
|
||||
|
||||
guard let localCallId = UUID(uuidString: localCallIdString) else {
|
||||
throw OWSAssertionError("unable to build localCallId. localCallIdString: \(localCallIdString)")
|
||||
}
|
||||
|
||||
callService.callUIAdapter.localHangupCall(localId: localCallId)
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
private class func markAsRead(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
|
||||
return firstly {
|
||||
self.notificationMessage(forUserInfo: userInfo)
|
||||
|
||||
@ -25,9 +25,6 @@ class DebugUINotifications: DebugUIPage, Dependencies {
|
||||
OWSTableItem(title: "All Notifications in Sequence") { [weak self] in
|
||||
self?.notifyForEverythingInSequence(contactThread: contactThread)
|
||||
},
|
||||
OWSTableItem(title: "Incoming Call") { [weak self] in
|
||||
self?.notifyForIncomingCall(thread: contactThread)
|
||||
},
|
||||
OWSTableItem(title: "Call Rejected: New Safety Number") { [weak self] in
|
||||
self?.notifyForMissedCallBecauseOfNewIdentity(thread: contactThread)
|
||||
},
|
||||
@ -88,9 +85,7 @@ class DebugUINotifications: DebugUIPage, Dependencies {
|
||||
func notifyForEverythingInSequence(contactThread: TSContactThread) -> Guarantee<Void> {
|
||||
let taskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
|
||||
|
||||
return self.notifyForIncomingCall(thread: contactThread).then {
|
||||
self.notifyForMissedCallBecauseOfNewIdentity(thread: contactThread)
|
||||
}.then {
|
||||
return self.notifyForMissedCallBecauseOfNewIdentity(thread: contactThread).then {
|
||||
self.notifyForMissedCallBecauseOfNoLongerVerifiedIdentity(thread: contactThread)
|
||||
}.then {
|
||||
self.notifyForIncomingMessage(thread: contactThread)
|
||||
@ -99,13 +94,6 @@ class DebugUINotifications: DebugUIPage, Dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func notifyForIncomingCall(thread: TSContactThread) -> Guarantee<Void> {
|
||||
return delayedNotificationDispatchWithFakeCall(thread: thread) { call in
|
||||
self.notificationPresenterImpl.presentIncomingCall(call, caller: thread.contactAddress)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func notifyForMissedCallBecauseOfNewIdentity(thread: TSContactThread) -> Guarantee<Void> {
|
||||
return delayedNotificationDispatchWithFakeCall(thread: thread) { call in
|
||||
|
||||
@ -187,9 +187,6 @@
|
||||
/* Title for the view of all your signal connections */
|
||||
"ALL_SIGNAL_CONNECTIONS_TITLE" = "All Connections";
|
||||
|
||||
/* notification action */
|
||||
"ANSWER_CALL_BUTTON_TITLE" = "Answer";
|
||||
|
||||
/* notification body */
|
||||
"APN_Message" = "You may have new messages";
|
||||
|
||||
@ -682,9 +679,6 @@
|
||||
/* Label for button that lets users call a contact again. */
|
||||
"CALL_AGAIN_BUTTON_TITLE" = "Call Again";
|
||||
|
||||
/* notification body */
|
||||
"CALL_AUDIO_INCOMING_NOTIFICATION_BODY" = "📞 Incoming voice call…";
|
||||
|
||||
/* notification body for a missed call in the last 24 hours. Embeds {{time}}, e.g. '3:30 PM'. */
|
||||
"CALL_AUDIO_MISSED_24_HOURS_NOTIFICATION_BODY_FORMAT" = "📞 Missed voice call at %@";
|
||||
|
||||
@ -712,12 +706,6 @@
|
||||
/* label for button shown when an incoming call rings */
|
||||
"CALL_CONTROLS_INCOMING_DECLINE" = "decline";
|
||||
|
||||
/* Body for the repeating notification shown while an incoming group call is ringing, if the user has chosen not to include contact names in notifications */
|
||||
"CALL_GROUP_INCOMING_NOTIFICATION_BODY_ANONYMOUS" = "📹 Incoming group call…";
|
||||
|
||||
/* Body for the repeating notification shown while an incoming group call is ringing. Embeds {{caller name}} */
|
||||
"CALL_GROUP_INCOMING_NOTIFICATION_BODY_FORMAT" = "📹 %@ is ringing the group…";
|
||||
|
||||
/* notification body */
|
||||
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
|
||||
|
||||
@ -742,9 +730,6 @@
|
||||
/* Title for alert offering to call a user. */
|
||||
"CALL_USER_ALERT_TITLE" = "Call?";
|
||||
|
||||
/* notification body */
|
||||
"CALL_VIDEO_INCOMING_NOTIFICATION_BODY" = "📹 Incoming video call…";
|
||||
|
||||
/* notification body for a missed call in the last 24 hours. Embeds {{time}}, e.g. '3:30 PM'. */
|
||||
"CALL_VIDEO_MISSED_24_HOURS_NOTIFICATION_BODY_FORMAT" = "📹 Missed video call at %@";
|
||||
|
||||
@ -5839,9 +5824,6 @@
|
||||
/* Error message when sending a verification code via voice call failed, but resending via sms might succeed. */
|
||||
"REGISTRATION_VOICE_CODE_FAILED_TRY_SMS_ERROR" = "We couldn't send you a verification code via voice call. Try receiving your code via sms instead.";
|
||||
|
||||
/* notification action */
|
||||
"REJECT_CALL_BUTTON_TITLE" = "Reject";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"RELAY_REGISTERED_ERROR_RECOVERY" = "The phone number you are trying to register has already been registered on another server, please unregister from there and try again.";
|
||||
|
||||
|
||||
@ -86,9 +86,6 @@ public enum StandardSound: UInt {
|
||||
case synth = 12
|
||||
case signalClassic = 13
|
||||
|
||||
// Ringtone Sounds
|
||||
case reflection = 14
|
||||
|
||||
// Calls
|
||||
case callConnecting = 15
|
||||
case callOutboundRinging = 16
|
||||
@ -107,8 +104,6 @@ public enum StandardSound: UInt {
|
||||
// Audio Playback
|
||||
case beginNextTrack = 24
|
||||
case endLastTrack = 25
|
||||
|
||||
public static let defaultiOSIncomingRingtone: StandardSound = .reflection
|
||||
}
|
||||
|
||||
public extension StandardSound {
|
||||
@ -135,9 +130,6 @@ public extension StandardSound {
|
||||
case .synth: return "Synth"
|
||||
case .signalClassic: return "Signal Classic"
|
||||
|
||||
// Ringtone Sounds
|
||||
case .reflection: return "Opening"
|
||||
|
||||
// Calls
|
||||
case .callConnecting: return "Call Connecting"
|
||||
case .callOutboundRinging: return "Call Outboung Ringing"
|
||||
@ -198,9 +190,6 @@ public extension StandardSound {
|
||||
case .signalClassic:
|
||||
return quiet ? "classic-quiet.aifc" : "classic.aifc"
|
||||
|
||||
// Ringtone Sounds
|
||||
case .reflection: return "Reflection.m4r"
|
||||
|
||||
// Calls
|
||||
case .callConnecting: return "ringback_tone_ansi.caf"
|
||||
case .callOutboundRinging: return "ringback_tone_ansi.caf"
|
||||
|
||||
@ -26,7 +26,6 @@ public enum AppNotificationCategory: CaseIterable {
|
||||
case incomingReactionWithActions_CanReply
|
||||
case incomingReactionWithActions_CannotReply
|
||||
case infoOrErrorMessage
|
||||
case incomingCall
|
||||
case missedCallWithActions
|
||||
case missedCallWithoutActions
|
||||
case missedCallFromNoLongerVerifiedIdentity
|
||||
@ -39,9 +38,7 @@ public enum AppNotificationCategory: CaseIterable {
|
||||
}
|
||||
|
||||
public enum AppNotificationAction: String, CaseIterable {
|
||||
case answerCall
|
||||
case callBack
|
||||
case declineCall
|
||||
case markAsRead
|
||||
case reply
|
||||
case showThread
|
||||
@ -61,7 +58,6 @@ public struct AppNotificationUserInfoKey {
|
||||
public static let storyTimestamp = "Signal.AppNotificationsUserInfoKey.storyTimestamp"
|
||||
public static let callBackAciString = "Signal.AppNotificationsUserInfoKey.callBackUuid"
|
||||
public static let callBackPhoneNumber = "Signal.AppNotificationsUserInfoKey.callBackPhoneNumber"
|
||||
public static let localCallId = "Signal.AppNotificationsUserInfoKey.localCallId"
|
||||
public static let isMissedCall = "Signal.AppNotificationsUserInfoKey.isMissedCall"
|
||||
public static let defaultAction = "Signal.AppNotificationsUserInfoKey.defaultAction"
|
||||
}
|
||||
@ -83,8 +79,6 @@ extension AppNotificationCategory {
|
||||
return "Signal.AppNotificationCategory.incomingReactionWithActionsNoReply"
|
||||
case .infoOrErrorMessage:
|
||||
return "Signal.AppNotificationCategory.infoOrErrorMessage"
|
||||
case .incomingCall:
|
||||
return "Signal.AppNotificationCategory.incomingCall"
|
||||
case .missedCallWithActions:
|
||||
return "Signal.AppNotificationCategory.missedCallWithActions"
|
||||
case .missedCallWithoutActions:
|
||||
@ -121,8 +115,6 @@ extension AppNotificationCategory {
|
||||
return []
|
||||
case .infoOrErrorMessage:
|
||||
return []
|
||||
case .incomingCall:
|
||||
return [.answerCall, .declineCall]
|
||||
case .missedCallWithActions:
|
||||
return [.callBack, .showThread]
|
||||
case .missedCallWithoutActions:
|
||||
@ -148,12 +140,8 @@ extension AppNotificationCategory {
|
||||
extension AppNotificationAction {
|
||||
var identifier: String {
|
||||
switch self {
|
||||
case .answerCall:
|
||||
return "Signal.AppNotifications.Action.answerCall"
|
||||
case .callBack:
|
||||
return "Signal.AppNotifications.Action.callBack"
|
||||
case .declineCall:
|
||||
return "Signal.AppNotifications.Action.declineCall"
|
||||
case .markAsRead:
|
||||
return "Signal.AppNotifications.Action.markAsRead"
|
||||
case .reply:
|
||||
@ -230,60 +218,6 @@ public class NotificationPresenterImpl: NotificationPresenter {
|
||||
}
|
||||
}
|
||||
|
||||
public func presentIncomingCall(_ call: CallNotificationInfo, caller: SignalServiceAddress) {
|
||||
let thread = call.thread
|
||||
|
||||
let callPreview: CallPreview?
|
||||
let callerNameForGroupCall: String?
|
||||
(callPreview, callerNameForGroupCall) = databaseStorage.read { tx in
|
||||
guard let callPreview = self.fetchCallPreview(thread: thread, tx: tx) else {
|
||||
return (nil, nil)
|
||||
}
|
||||
return (
|
||||
callPreview,
|
||||
thread.isGroupThread ? contactManager.displayName(for: caller, tx: tx).resolvedValue() : nil
|
||||
)
|
||||
}
|
||||
|
||||
let notificationBody: String
|
||||
if let callerNameForGroupCall = callerNameForGroupCall {
|
||||
notificationBody = String(format: NotificationStrings.incomingGroupCallBodyFormat, callerNameForGroupCall)
|
||||
} else if thread.isGroupThread {
|
||||
notificationBody = NotificationStrings.incomingGroupCallBodyAnonymous
|
||||
} else {
|
||||
switch call.offerMediaType {
|
||||
case .audio: notificationBody = NotificationStrings.incomingAudioCallBody
|
||||
case .video: notificationBody = NotificationStrings.incomingVideoCallBody
|
||||
}
|
||||
}
|
||||
|
||||
let userInfo = [
|
||||
AppNotificationUserInfoKey.threadId: thread.uniqueId,
|
||||
AppNotificationUserInfoKey.localCallId: call.localId.uuidString
|
||||
]
|
||||
|
||||
var interaction: INInteraction?
|
||||
if callPreview != nil, let intent = thread.generateIncomingCallIntent(callerAddress: caller) {
|
||||
let wrapper = INInteraction(intent: intent, response: nil)
|
||||
wrapper.direction = .incoming
|
||||
interaction = wrapper
|
||||
}
|
||||
|
||||
performNotificationActionAsync { completion in
|
||||
self.presenter.notify(
|
||||
category: .incomingCall,
|
||||
title: callPreview?.notificationTitle,
|
||||
body: notificationBody,
|
||||
threadIdentifier: callPreview?.threadIdentifier,
|
||||
userInfo: userInfo,
|
||||
interaction: interaction,
|
||||
sound: nil,
|
||||
replacingIdentifier: call.localId.uuidString,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Classifies a timestamp based on how it should be included in a notification.
|
||||
///
|
||||
/// In particular, a notification already comes with its own timestamp, so any information we put in has to be
|
||||
|
||||
@ -30,13 +30,6 @@ public class UserNotificationConfig {
|
||||
|
||||
class func notificationAction(_ action: AppNotificationAction) -> UNNotificationAction? {
|
||||
switch action {
|
||||
case .answerCall:
|
||||
return notificationActionWithIdentifier(
|
||||
action.identifier,
|
||||
title: CallStrings.answerCallButtonTitle,
|
||||
options: [.foreground],
|
||||
systemImage: "phone"
|
||||
)
|
||||
case .callBack:
|
||||
return notificationActionWithIdentifier(
|
||||
action.identifier,
|
||||
@ -44,13 +37,6 @@ public class UserNotificationConfig {
|
||||
options: [.foreground],
|
||||
systemImage: "phone"
|
||||
)
|
||||
case .declineCall:
|
||||
return notificationActionWithIdentifier(
|
||||
action.identifier,
|
||||
title: CallStrings.declineCallButtonTitle,
|
||||
options: [],
|
||||
systemImage: "phone.down"
|
||||
)
|
||||
case .markAsRead:
|
||||
return notificationActionWithIdentifier(
|
||||
action.identifier,
|
||||
@ -312,7 +298,6 @@ class UserNotificationPresenter: Dependencies {
|
||||
private func shouldPresentNotification(category: AppNotificationCategory, userInfo: [AnyHashable: Any]) -> Bool {
|
||||
switch category {
|
||||
case .incomingMessageFromNoLongerVerifiedIdentity,
|
||||
.incomingCall,
|
||||
.missedCallWithActions,
|
||||
.missedCallWithoutActions,
|
||||
.missedCallFromNoLongerVerifiedIdentity,
|
||||
|
||||
@ -339,22 +339,6 @@ public class MessageStrings: NSObject {
|
||||
|
||||
public class NotificationStrings: NSObject {
|
||||
|
||||
static public var incomingAudioCallBody: String {
|
||||
OWSLocalizedString("CALL_AUDIO_INCOMING_NOTIFICATION_BODY", comment: "notification body")
|
||||
}
|
||||
|
||||
static public var incomingVideoCallBody: String {
|
||||
OWSLocalizedString("CALL_VIDEO_INCOMING_NOTIFICATION_BODY", comment: "notification body")
|
||||
}
|
||||
|
||||
static public var incomingGroupCallBodyFormat: String {
|
||||
OWSLocalizedString("CALL_GROUP_INCOMING_NOTIFICATION_BODY_FORMAT", comment: "Body for the repeating notification shown while an incoming group call is ringing. Embeds {{caller name}}")
|
||||
}
|
||||
|
||||
static public var incomingGroupCallBodyAnonymous: String {
|
||||
OWSLocalizedString("CALL_GROUP_INCOMING_NOTIFICATION_BODY_ANONYMOUS", comment: "Body for the repeating notification shown while an incoming group call is ringing, if the user has chosen not to include contact names in notifications")
|
||||
}
|
||||
|
||||
static public var missedCallBecauseOfIdentityChangeBody: String {
|
||||
OWSLocalizedString("CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY",
|
||||
comment: "notification body")
|
||||
@ -503,14 +487,6 @@ public class CallStrings: NSObject {
|
||||
static public var showThreadButtonTitle: String {
|
||||
OWSLocalizedString("SHOW_THREAD_BUTTON_TITLE", comment: "notification action")
|
||||
}
|
||||
|
||||
static public var answerCallButtonTitle: String {
|
||||
OWSLocalizedString("ANSWER_CALL_BUTTON_TITLE", comment: "notification action")
|
||||
}
|
||||
|
||||
static public var declineCallButtonTitle: String {
|
||||
OWSLocalizedString("REJECT_CALL_BUTTON_TITLE", comment: "notification action")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@ -10,7 +10,7 @@ extension Sounds {
|
||||
private static func shouldAudioPlayerLoop(forSound sound: Sound) -> Bool {
|
||||
guard case .standard(let standardSound) = sound else { return false }
|
||||
switch standardSound {
|
||||
case .callConnecting, .callOutboundRinging, .defaultiOSIncomingRingtone:
|
||||
case .callConnecting, .callOutboundRinging:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user