add incoming/outgoing call message bubble

pull/560/head
ryanzhao 4 years ago
parent 383f996e82
commit 23fb69ba6f

@ -138,6 +138,7 @@
76EB054018170B33006006FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB03C318170B33006006FC /* AppDelegate.m */; };
7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */; };
7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */; };
7B7CB189270430D20079FF93 /* CallMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB188270430D20079FF93 /* CallMessageView.swift */; };
7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; };
7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7BCD116C27016062006330F1 /* WebRTCSession+DataChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */; };
@ -1112,6 +1113,7 @@
7B2DB2AD26F1B0FF0035B509 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Localizable.strings; sourceTree = "<group>"; };
7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsendRequest.swift; sourceTree = "<group>"; };
7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessageView.swift; sourceTree = "<group>"; };
7B7CB188270430D20079FF93 /* CallMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMessageView.swift; sourceTree = "<group>"; };
7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
7BC01A3F241F40AB00BC7C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -2114,6 +2116,7 @@
B8D84EA225DF745A005A043E /* LinkPreviewState.swift */,
B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */,
7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */,
7B7CB188270430D20079FF93 /* CallMessageView.swift */,
);
path = "Content Views";
sourceTree = "<group>";
@ -4899,6 +4902,7 @@
34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */,
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */,
34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */,
7B7CB189270430D20079FF93 /* CallMessageView.swift in Sources */,
C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */,
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */,
3488F9362191CC4000E524CC /* MediaView.swift in Sources */,

@ -15,6 +15,7 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
OWSMessageCellType_GenericAttachment,
OWSMessageCellType_MediaMessage,
OWSMessageCellType_OversizeTextDownloading,
OWSMessageCellType_CallMessage,
OWSMessageCellType_DeletedMessage
};

@ -32,6 +32,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return @"OWSMessageCellType_MediaMessage";
case OWSMessageCellType_OversizeTextDownloading:
return @"OWSMessageCellType_OversizeTextDownloading";
case OWSMessageCellType_CallMessage:
return @"OWSMessageCellType_CallMessage";
case OWSMessageCellType_DeletedMessage:
return @"OWSMessageCellType_DeletedMessage";
}
}
@ -475,6 +479,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.messageCellType = OWSMessageCellType_DeletedMessage;
return;
}
if (message.isCallMessage) {
self.messageCellType = OWSMessageCellType_CallMessage;
return;
}
// Check for quoted replies _before_ media album handling,
// since that logic may exit early.

@ -0,0 +1,51 @@
final class CallMessageView : UIView {
private let viewItem: ConversationViewItem
private let textColor: UIColor
// MARK: Settings
private static let iconSize: CGFloat = 24
private static let iconImageViewSize: CGFloat = 40
// MARK: Lifecycle
init(viewItem: ConversationViewItem, textColor: UIColor) {
self.viewItem = viewItem
self.textColor = textColor
super.init(frame: CGRect.zero)
setUpViewHierarchy()
}
override init(frame: CGRect) {
preconditionFailure("Use init(viewItem:textColor:) instead.")
}
required init?(coder: NSCoder) {
preconditionFailure("Use init(viewItem:textColor:) instead.")
}
private func setUpViewHierarchy() {
guard let message = viewItem.interaction as? TSMessage else { preconditionFailure() }
// Image view
let iconSize = CallMessageView.iconSize
let icon = UIImage(named: "Phone")?.withTint(textColor)?.resizedImage(to: CGSize(width: iconSize, height: iconSize))
let imageView = UIImageView(image: icon)
imageView.contentMode = .center
let iconImageViewSize = CallMessageView.iconImageViewSize
imageView.set(.width, to: iconImageViewSize)
imageView.set(.height, to: iconImageViewSize)
// Body label
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = message.body
titleLabel.textColor = textColor
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
// Stack view
let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
stackView.axis = .horizontal
stackView.alignment = .center
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 12)
addSubview(stackView)
stackView.pin(to: self, withInset: Values.smallSpacing)
}
}

@ -275,7 +275,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
timerViewOutgoingMessageConstraint.isActive = (direction == .outgoing)
timerViewIncomingMessageConstraint.isActive = (direction == .incoming)
// Swipe to reply
if (message.isDeleted) {
if (message.isDeleted || message.isCallMessage) {
removeGestureRecognizer(panGestureRecognizer)
} else {
addGestureRecognizer(panGestureRecognizer)
@ -398,6 +398,10 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
let deletedMessageView = DeletedMessageView(viewItem: viewItem, textColor: bodyLabelTextColor)
snContentView.addSubview(deletedMessageView)
deletedMessageView.pin(to: snContentView)
case .callMessage:
let callMessageView = CallMessageView(viewItem: viewItem, textColor: bodyLabelTextColor)
snContentView.addSubview(callMessageView)
callMessageView.pin(to: snContentView)
default: return
}
}

@ -19,7 +19,6 @@ extension AppDelegate {
conversationVC.inputAccessoryView?.alpha = 0
presentingVC.present(callVC, animated: true, completion: nil)
} else {
}
}
}

@ -572,3 +572,5 @@
"OPEN_SETTINGS_BUTTON" = "Settings";
"voice_call" = "Voice Call";
"video_call" = "Video Call";
"call_outgoing" = "Outgoing Call";
"call_incoming" = "Incoming Call";

@ -119,6 +119,8 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
let message = CallMessage()
message.kind = .offer
message.sdps = [ sdp.sdp ]
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
tsMessage.save(with: transaction)
MessageSender.sendNonDurably(message, in: thread, using: transaction).done2 {
seal.fulfill(())
}.catch2 { error in

@ -1,5 +1,26 @@
public extension TSIncomingMessage {
static func from(_ callMessage: CallMessage, associatedWith thread: TSThread) -> TSIncomingMessage {
let sender = callMessage.sender!
let result = TSIncomingMessage(
timestamp: callMessage.sentTimestamp!,
in: thread,
authorId: sender,
sourceDeviceId: 1,
messageBody: NSLocalizedString("call_incoming", comment: ""),
attachmentIds: [],
expiresInSeconds: 0,
quotedMessage: nil,
linkPreview: nil,
wasReceivedByUD: true,
openGroupInvitationName: nil,
openGroupInvitationURL: nil,
serverHash: callMessage.serverHash
)
result.isCallMessage = true
return result
}
static func from(_ visibleMessage: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, associatedWith thread: TSThread) -> TSIncomingMessage {
let sender = visibleMessage.sender!

@ -40,6 +40,7 @@ extern const NSUInteger kOversizeTextMessageSizeThreshold;
@property (nonatomic, readonly, nullable) NSString *openGroupInvitationURL;
@property (nonatomic, nullable) NSString *serverHash;
@property (nonatomic) BOOL isDeleted;
@property (nonatomic) BOOL isCallMessage;
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;

@ -87,6 +87,7 @@ const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
_openGroupInvitationURL = openGroupInvitationURL;
_serverHash = serverHash;
_isDeleted = false;
_isCallMessage = false;
return self;
}

@ -2,6 +2,13 @@ import SessionUtilitiesKit
@objc public extension TSOutgoingMessage {
@objc(fromCallOffer:associatedWith:)
static func from(_ callMessage: CallMessage, associatedWith thread: TSThread) -> TSOutgoingMessage {
let outgoingMessage = TSOutgoingMessage(in: thread, messageBody: NSLocalizedString("call_outgoing", comment: ""), attachmentId: nil)
outgoingMessage.isCallMessage = true
return outgoingMessage
}
@objc(from:associatedWith:)
static func from(_ visibleMessage: VisibleMessage, associatedWith thread: TSThread) -> TSOutgoingMessage {
return from(visibleMessage, associatedWith: thread, using: nil)

@ -280,6 +280,13 @@ extension MessageReceiver {
// Delegate to the main app, which is expected to show a dialog confirming
// that the user wants to pick up the call. When they do, the SDP contained
// in the offer message will be passed to WebRTCSession.handleRemoteSDP(_:from:).
let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
if let threadID = storage.getOrCreateThread(for: message.sender!, groupPublicKey: message.groupPublicKey, openGroupID: nil, using: transaction),
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) {
let tsMessage = TSIncomingMessage.from(message, associatedWith: thread)
tsMessage.save(with: transaction)
}
handleOfferCallMessage?(message)
case .answer:
print("[Calls] Received answer message.")

Loading…
Cancel
Save