Path: blob/main/SignalServiceKit/Calls/OutgoingCallEventSyncMessage.swift
1 views
//
// 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
}
}
}