205 lines
6.4 KiB
Swift
205 lines
6.4 KiB
Swift
//
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
/// Represents a call event that occurred on this device that we want to
|
|
/// communicate to our linked devices.
|
|
@objc(OutgoingCallEvent)
|
|
class OutgoingCallEvent: NSObject, NSSecureCoding {
|
|
enum CallType: UInt {
|
|
case audio
|
|
case video
|
|
case group
|
|
case adHocCall
|
|
}
|
|
|
|
enum EventDirection: UInt {
|
|
case incoming
|
|
case outgoing
|
|
}
|
|
|
|
enum EventType: UInt {
|
|
case accepted
|
|
case notAccepted
|
|
case deleted
|
|
case observed
|
|
}
|
|
|
|
let timestamp: UInt64
|
|
let conversationId: Data
|
|
let callId: UInt64
|
|
let callType: CallType
|
|
let eventDirection: EventDirection
|
|
let eventType: EventType
|
|
|
|
init(
|
|
timestamp: UInt64,
|
|
conversationId: Data,
|
|
callId: UInt64,
|
|
callType: CallType,
|
|
eventDirection: EventDirection,
|
|
eventType: EventType,
|
|
) {
|
|
self.timestamp = timestamp
|
|
self.conversationId = conversationId
|
|
self.callId = callId
|
|
self.callType = callType
|
|
self.eventDirection = eventDirection
|
|
self.eventType = eventType
|
|
}
|
|
|
|
// MARK: NSSecureCoding
|
|
|
|
private enum Keys {
|
|
static let timestamp = "timestamp"
|
|
static let conversationId = "peerUuid"
|
|
static let callId = "callId"
|
|
static let callType = "type"
|
|
static let eventDirection = "direction"
|
|
static let eventType = "event"
|
|
}
|
|
|
|
static var supportsSecureCoding: Bool { true }
|
|
|
|
func encode(with coder: NSCoder) {
|
|
coder.encode(NSNumber(value: timestamp), forKey: Keys.timestamp)
|
|
coder.encode(conversationId as NSData, forKey: Keys.conversationId)
|
|
coder.encode(NSNumber(value: callId), forKey: Keys.callId)
|
|
coder.encode(NSNumber(value: callType.rawValue), forKey: Keys.callType)
|
|
coder.encode(NSNumber(value: eventDirection.rawValue), forKey: Keys.eventDirection)
|
|
coder.encode(NSNumber(value: eventType.rawValue), forKey: Keys.eventType)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
guard
|
|
let timestamp = coder.decodeObject(of: NSNumber.self, forKey: Keys.timestamp)?.uint64Value,
|
|
let conversationId = coder.decodeObject(of: NSData.self, forKey: Keys.conversationId) as Data?,
|
|
let callId = coder.decodeObject(of: NSNumber.self, forKey: Keys.callId)?.uint64Value,
|
|
let callTypeRaw = coder.decodeObject(of: NSNumber.self, forKey: Keys.callType)?.uintValue,
|
|
let callType = CallType(rawValue: callTypeRaw),
|
|
let eventDirectionRaw = coder.decodeObject(of: NSNumber.self, forKey: Keys.eventDirection)?.uintValue,
|
|
let eventDirection = EventDirection(rawValue: eventDirectionRaw),
|
|
let eventTypeRaw = coder.decodeObject(of: NSNumber.self, forKey: Keys.eventType)?.uintValue,
|
|
let eventType = EventType(rawValue: eventTypeRaw)
|
|
else {
|
|
owsFailDebug("Missing or unrecognized fields!")
|
|
return nil
|
|
}
|
|
|
|
self.timestamp = timestamp
|
|
self.conversationId = conversationId
|
|
self.callId = callId
|
|
self.callType = callType
|
|
self.eventDirection = eventDirection
|
|
self.eventType = eventType
|
|
}
|
|
}
|
|
|
|
/// Represents a sync message containing a "call event".
|
|
///
|
|
/// Indicates to linked devices that a call has changed state. For example, that
|
|
/// we accepted a ringing call on this device.
|
|
///
|
|
/// - SeeAlso ``IncomingCallEventSyncMessageManager``
|
|
@objc(OutgoingCallEventSyncMessage)
|
|
public class OutgoingCallEventSyncMessage: OutgoingSyncMessage {
|
|
override public class var supportsSecureCoding: Bool { true }
|
|
|
|
public required init?(coder: NSCoder) {
|
|
guard let callEvent = coder.decodeObject(of: OutgoingCallEvent.self, forKey: "event") else {
|
|
return nil
|
|
}
|
|
self.callEvent = callEvent
|
|
super.init(coder: coder)
|
|
}
|
|
|
|
override public func encode(with coder: NSCoder) {
|
|
super.encode(with: coder)
|
|
coder.encode(callEvent, forKey: "event")
|
|
}
|
|
|
|
override public var hash: Int {
|
|
var hasher = Hasher()
|
|
hasher.combine(super.hash)
|
|
hasher.combine(callEvent)
|
|
return hasher.finalize()
|
|
}
|
|
|
|
override public func isEqual(_ object: Any?) -> Bool {
|
|
guard let object = object as? Self else { return false }
|
|
guard super.isEqual(object) else { return false }
|
|
guard self.callEvent == object.callEvent else { return false }
|
|
return true
|
|
}
|
|
|
|
/// The call event.
|
|
let callEvent: OutgoingCallEvent
|
|
|
|
init(
|
|
localThread: TSContactThread,
|
|
event: OutgoingCallEvent,
|
|
tx: DBReadTransaction,
|
|
) {
|
|
self.callEvent = event
|
|
super.init(localThread: localThread, tx: tx)
|
|
}
|
|
|
|
override public var isUrgent: Bool { false }
|
|
|
|
override public func syncMessageBuilder(tx: DBReadTransaction) -> SSKProtoSyncMessageBuilder? {
|
|
let callEventBuilder = SSKProtoSyncMessageCallEvent.builder()
|
|
callEventBuilder.setCallID(callEvent.callId)
|
|
callEventBuilder.setType(callEvent.callType.protoValue)
|
|
callEventBuilder.setDirection(callEvent.eventDirection.protoValue)
|
|
callEventBuilder.setEvent(callEvent.eventType.protoValue)
|
|
callEventBuilder.setTimestamp(callEvent.timestamp)
|
|
callEventBuilder.setConversationID(callEvent.conversationId)
|
|
|
|
let builder = SSKProtoSyncMessage.builder()
|
|
builder.setCallEvent(callEventBuilder.buildInfallibly())
|
|
return builder
|
|
}
|
|
}
|
|
|
|
private extension OutgoingCallEvent.CallType {
|
|
var protoValue: SSKProtoSyncMessageCallEventType {
|
|
switch self {
|
|
case .audio:
|
|
return .audioCall
|
|
case .video:
|
|
return .videoCall
|
|
case .group:
|
|
return .groupCall
|
|
case .adHocCall:
|
|
return .adHocCall
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension OutgoingCallEvent.EventDirection {
|
|
var protoValue: SSKProtoSyncMessageCallEventDirection {
|
|
switch self {
|
|
case .incoming:
|
|
return .incoming
|
|
case .outgoing:
|
|
return .outgoing
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension OutgoingCallEvent.EventType {
|
|
var protoValue: SSKProtoSyncMessageCallEventEvent {
|
|
switch self {
|
|
case .accepted:
|
|
return .accepted
|
|
case .notAccepted:
|
|
return .notAccepted
|
|
case .deleted:
|
|
return .deleted
|
|
case .observed:
|
|
return .observed
|
|
}
|
|
}
|
|
}
|