//// //// Copyright (c) 2019 Open Whisper Systems. All rights reserved. //// // //import Foundation //import SignalUtilitiesKit // //public enum CallState: String { // case idle // case dialing // case answering // case remoteRinging // case localRinging // case connected // case reconnecting // case localFailure // terminal // case localHangup // terminal // case remoteHangup // terminal // case remoteBusy // terminal //} // //enum CallDirection { // case outgoing, incoming //} // //// All Observer methods will be invoked from the main thread. //protocol CallObserver: class { // func stateDidChange(call: SignalCall, state: CallState) // func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool) // func muteDidChange(call: SignalCall, isMuted: Bool) // func holdDidChange(call: SignalCall, isOnHold: Bool) // func audioSourceDidChange(call: SignalCall, audioSource: AudioSource?) //} // ///** // * Data model for a WebRTC backed voice/video call. // * // * This class' state should only be accessed on the main queue. // */ //@objc public class SignalCall: NSObject { // // var observers = [Weak]() // // @objc // let remotePhoneNumber: String // // var isTerminated: Bool { // switch state { // case .localFailure, .localHangup, .remoteHangup, .remoteBusy: // return true // case .idle, .dialing, .answering, .remoteRinging, .localRinging, .connected, .reconnecting: // return false // } // } // // // Signal Service identifier for this Call. Used to coordinate the call across remote clients. // let signalingId: UInt64 // // let direction: CallDirection // // // Distinguishes between calls locally, e.g. in CallKit // @objc // let localId: UUID // // let thread: TSContactThread // // var callRecord: TSCall? { // didSet { // AssertIsOnMainThread() // assert(oldValue == nil) // // updateCallRecordType() // } // } // // var hasLocalVideo = false { // didSet { // AssertIsOnMainThread() // // for observer in observers { // observer.value?.hasLocalVideoDidChange(call: self, hasLocalVideo: hasLocalVideo) // } // } // } // // var state: CallState { // didSet { // AssertIsOnMainThread() // Logger.debug("state changed: \(oldValue) -> \(self.state) for call: \(self.identifiersForLogs)") // // // Update connectedDate // if case .connected = self.state { // // if it's the first time we've connected (not a reconnect) // if connectedDate == nil { // connectedDate = NSDate() // } // } // // updateCallRecordType() // // for observer in observers { // observer.value?.stateDidChange(call: self, state: state) // } // } // } // // var isMuted = false { // didSet { // AssertIsOnMainThread() // // Logger.debug("muted changed: \(oldValue) -> \(self.isMuted)") // // for observer in observers { // observer.value?.muteDidChange(call: self, isMuted: isMuted) // } // } // } // // let audioActivity: AudioActivity // // var audioSource: AudioSource? = nil { // didSet { // AssertIsOnMainThread() // Logger.debug("audioSource changed: \(String(describing: oldValue)) -> \(String(describing: audioSource))") // // for observer in observers { // observer.value?.audioSourceDidChange(call: self, audioSource: audioSource) // } // } // } // // var isSpeakerphoneEnabled: Bool { // guard let audioSource = self.audioSource else { // return false // } // // return audioSource.isBuiltInSpeaker // } // // var isOnHold = false { // didSet { // AssertIsOnMainThread() // Logger.debug("isOnHold changed: \(oldValue) -> \(self.isOnHold)") // // for observer in observers { // observer.value?.holdDidChange(call: self, isOnHold: isOnHold) // } // } // } // // var connectedDate: NSDate? // // var error: CallError? // // // MARK: Initializers and Factory Methods // // init(direction: CallDirection, localId: UUID, signalingId: UInt64, state: CallState, remotePhoneNumber: String) { // self.direction = direction // self.localId = localId // self.signalingId = signalingId // self.state = state // self.remotePhoneNumber = remotePhoneNumber // self.thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) // self.audioActivity = AudioActivity(audioDescription: "[SignalCall] with \(remotePhoneNumber)", behavior: .call) // } // // // A string containing the three identifiers for this call. // var identifiersForLogs: String { // return "{\(remotePhoneNumber), \(localId), \(signalingId)}" // } // // class func outgoingCall(localId: UUID, remotePhoneNumber: String) -> SignalCall { // return SignalCall(direction: .outgoing, localId: localId, signalingId: newCallSignalingId(), state: .dialing, remotePhoneNumber: remotePhoneNumber) // } // // class func incomingCall(localId: UUID, remotePhoneNumber: String, signalingId: UInt64) -> SignalCall { // return SignalCall(direction: .incoming, localId: localId, signalingId: signalingId, state: .answering, remotePhoneNumber: remotePhoneNumber) // } // // // - // // func addObserverAndSyncState(observer: CallObserver) { // AssertIsOnMainThread() // // observers.append(Weak(value: observer)) // // // Synchronize observer with current call state // observer.stateDidChange(call: self, state: state) // } // // func removeObserver(_ observer: CallObserver) { // AssertIsOnMainThread() // // while let index = observers.firstIndex(where: { $0.value === observer }) { // observers.remove(at: index) // } // } // // func removeAllObservers() { // AssertIsOnMainThread() // // observers = [] // } // // private func updateCallRecordType() { // AssertIsOnMainThread() // // guard let callRecord = self.callRecord else { // return // } // // // Mark incomplete calls as completed if call has connected. // if state == .connected && // callRecord.callType == RPRecentCallTypeOutgoingIncomplete { // callRecord.updateCallType(RPRecentCallTypeOutgoing) // } // if state == .connected && // callRecord.callType == RPRecentCallTypeIncomingIncomplete { // callRecord.updateCallType(RPRecentCallTypeIncoming) // } // } // // // MARK: Equatable // // static func == (lhs: SignalCall, rhs: SignalCall) -> Bool { // return lhs.localId == rhs.localId // } // // static func newCallSignalingId() -> UInt64 { // return UInt64.ows_random() // } // // // This method should only be called when the call state is "connected". // func connectionDuration() -> TimeInterval { // return -connectedDate!.timeIntervalSinceNow // } //} // //fileprivate extension UInt64 { // static func ows_random() -> UInt64 { // return Cryptography.randomUInt64() // } //}