Reject incoming calls from an unseen changed identity

// FREEBIE
This commit is contained in:
Michael Kirk 2017-05-31 08:49:30 -07:00
parent 6b4a5398ef
commit 130aa132a0
14 changed files with 272 additions and 47 deletions

View File

@ -5,7 +5,7 @@ target 'Signal' do
pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git'
pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git'
#pod 'AxolotlKit', path: '../SignalProtocolKit'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/reject-unseen-id-calls'
#pod 'SignalServiceKit', path: '../SignalServiceKit'
pod 'OpenSSL'
pod 'JSQMessagesViewController', git: 'https://github.com/WhisperSystems/JSQMessagesViewController.git', branch: 'mkirk/position-edit-menu'

View File

@ -114,7 +114,7 @@ DEPENDENCIES:
- OpenSSL
- PureLayout
- Reachability
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/reject-unseen-id-calls`)
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
EXTERNAL SOURCES:
@ -124,6 +124,7 @@ EXTERNAL SOURCES:
:branch: mkirk/position-edit-menu
:git: https://github.com/WhisperSystems/JSQMessagesViewController.git
SignalServiceKit:
:branch: mkirk/reject-unseen-id-calls
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
@ -136,7 +137,7 @@ CHECKOUT OPTIONS:
:commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308
:git: https://github.com/WhisperSystems/JSQMessagesViewController.git
SignalServiceKit:
:commit: 0c46288cf96c6dadc775c2c2d089245d65490e78
:commit: e10cc0c1803c598a7518b6c8b28195e20f0eb12e
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf
@ -164,6 +165,6 @@ SPEC CHECKSUMS:
UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d
YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266
PODFILE CHECKSUM: 48e80d7f1e049bbf544a689fdfdf33e8196c640a
PODFILE CHECKSUM: 0e50a094f857d1833a01f162c8395b89604b2c35
COCOAPODS: 1.2.1

View File

@ -482,7 +482,7 @@
34D8C0231ED3673300188D7C /* DebugUIMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMessages.h; sourceTree = "<group>"; };
34D8C0241ED3673300188D7C /* DebugUIMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMessages.m; sourceTree = "<group>"; };
34D8C0251ED3673300188D7C /* DebugUITableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUITableViewController.h; sourceTree = "<group>"; };
34D8C0261ED3673300188D7C /* DebugUITableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUITableViewController.m; sourceTree = "<group>"; };
34D8C0261ED3673300188D7C /* DebugUITableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DebugUITableViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
34D8C0291ED3685800188D7C /* DebugUIContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIContacts.h; sourceTree = "<group>"; };
34D8C02A1ED3685800188D7C /* DebugUIContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIContacts.m; sourceTree = "<group>"; };
34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = "<group>"; };
@ -512,7 +512,7 @@
4516E3FE1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS101ExistingUsersBlockOnIdentityChange.m; path = Migrations/OWS101ExistingUsersBlockOnIdentityChange.m; sourceTree = "<group>"; };
451764281DE939FD00EDB8B9 /* ContactCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactCell.xib; sourceTree = "<group>"; };
451764291DE939FD00EDB8B9 /* ContactCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactCell.swift; sourceTree = "<group>"; };
451A13B01E13DED2000A50FD /* CallNotificationsAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CallNotificationsAdapter.swift; path = ../UserInterface/Notifications/CallNotificationsAdapter.swift; sourceTree = "<group>"; };
451A13B01E13DED2000A50FD /* CallNotificationsAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = CallNotificationsAdapter.swift; path = ../UserInterface/Notifications/CallNotificationsAdapter.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
451DE9F11DC1585F00810E42 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/iOS/PromiseKit.framework; sourceTree = "<group>"; };
451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SyncPushTokensJob.swift; path = Models/SyncPushTokensJob.swift; sourceTree = "<group>"; };
4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; };
@ -548,7 +548,7 @@
45666F7C1D9C0814008FE134 /* OWSDatabaseMigrationRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigrationRunner.h; path = Migrations/OWSDatabaseMigrationRunner.h; sourceTree = "<group>"; };
45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigrationRunner.m; path = Migrations/OWSDatabaseMigrationRunner.m; sourceTree = "<group>"; };
456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerConnectionClientTest.swift; sourceTree = "<group>"; };
4574A5D51DD6704700C6B692 /* CallService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallService.swift; sourceTree = "<group>"; };
4574A5D51DD6704700C6B692 /* CallService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CallService.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
4579431C1E7C8CE9008ED0C0 /* Pastelog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pastelog.h; sourceTree = "<group>"; };
4579431D1E7C8CE9008ED0C0 /* Pastelog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Pastelog.m; sourceTree = "<group>"; };
45794E851E00620000066731 /* CallUIAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CallUIAdapter.swift; path = UserInterface/CallUIAdapter.swift; sourceTree = "<group>"; };
@ -601,8 +601,8 @@
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompareSafetyNumbersActivity.swift; sourceTree = "<group>"; };
45E282DE1D08E67800ADD4C8 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = translations/gl.lproj/Localizable.strings; sourceTree = "<group>"; };
45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = "<group>"; };
45E2E91E1E13EE3500457AA0 /* OWSCallNotificationsAdaptee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSCallNotificationsAdaptee.h; path = UserInterface/OWSCallNotificationsAdaptee.h; sourceTree = "<group>"; };
45E2E91F1E153B3D00457AA0 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Strings.swift; path = UserInterface/Strings.swift; sourceTree = "<group>"; };
45E2E91E1E13EE3500457AA0 /* OWSCallNotificationsAdaptee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = OWSCallNotificationsAdaptee.h; path = UserInterface/OWSCallNotificationsAdaptee.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
45E2E91F1E153B3D00457AA0 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = Strings.swift; path = UserInterface/Strings.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilter.swift; sourceTree = "<group>"; };
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilterTest.swift; sourceTree = "<group>"; };
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSession.swift; sourceTree = "<group>"; };

View File

@ -1,5 +1,6 @@
// Created by Michael Kirk on 12/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -34,4 +35,9 @@ class CallNotificationsAdapter: NSObject {
Logger.debug("\(TAG) in \(#function)")
adaptee.presentMissedCall(call, callerName: callerName)
}
func presentRejectedCallWithUnseenIdentityChange(_ call: SignalCall, callerName: String) {
Logger.debug("\(TAG) in \(#function)")
adaptee.presentRejectedCallWithUnseenIdentityChange(call, callerName: callerName)
}
}

View File

@ -13,14 +13,17 @@ import UserNotifications
@available(iOS 10.0, *)
struct AppNotifications {
enum Category {
case missedCall
case missedCall,
rejectedCallFromUnseenIdentity
// Don't forget to update this! We use it to register categories.
static let allValues = [ missedCall ]
static let allValues = [ missedCall, rejectedCallFromUnseenIdentity ]
}
enum Action {
case callBack
case callBack,
showThread,
confirmIdentityAndCallBack
}
static var allCategories: Set<UNNotificationCategory> {
@ -35,6 +38,12 @@ struct AppNotifications {
actions: [ action(.callBack) ],
intentIdentifiers: [],
options: [])
case .rejectedCallFromUnseenIdentity:
return UNNotificationCategory(identifier: "org.whispersystems.signal.AppNotifications.Category.rejectedCallFromUnseenIdentity",
actions: [ action(.confirmIdentityAndCallBack), action(.showThread) ],
intentIdentifiers: [],
options: [])
}
}
@ -44,6 +53,14 @@ struct AppNotifications {
return UNNotificationAction(identifier: "org.whispersystems.signal.AppNotifications.Action.callBack",
title: CallStrings.callBackButtonTitle,
options: .authenticationRequired)
case .showThread:
return UNNotificationAction(identifier: "org.whispersystems.signal.AppNotifications.Action.showThread",
title: CallStrings.showThreadButtonTitle,
options: .authenticationRequired)
case .confirmIdentityAndCallBack:
return UNNotificationAction(identifier: "org.whispersystems.signal.AppNotifications.Action.confirmIdentityAndCallBack",
title: CallStrings.confirmIdentityAndCallBackButtonTitle,
options: .authenticationRequired)
}
}
}
@ -88,7 +105,7 @@ class UserNotificationsAdaptee: NSObject, OWSCallNotificationsAdaptee, UNUserNot
public func presentIncomingCall(_ call: SignalCall, callerName: String) {
Logger.debug("\(TAG) \(#function) is no-op, because it's handled with callkit.")
// TODO since CallKit doesn't currently work on the simulator,
// we could implement UNNotifications for simulator testing.
// we could implement UNNotifications for simulator testing, or if people have opted out of callkit.
}
public func presentMissedCall(_ call: SignalCall, callerName: String) {
@ -116,4 +133,30 @@ class UserNotificationsAdaptee: NSObject, OWSCallNotificationsAdaptee, UNUserNot
center.add(request)
}
func presentRejectedCallWithUnseenIdentityChange(_ call: SignalCall, callerName: String) {
Logger.debug("\(TAG) \(#function)")
let content = UNMutableNotificationContent()
// TODO group by thread identifier
// content.threadIdentifier = threadId
let notificationBody = { () -> String in
switch previewType {
case .noNameNoPreview:
return CallStrings.rejectedCallWithUnseenIdentityChangeNotificationBody
case .nameNoPreview, .namePreview:
return (Environment.getCurrent().preferences.isCallKitPrivacyEnabled()
? CallStrings.rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName
: String(format: CallStrings.rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName, callerName))
}}()
content.body = notificationBody
content.sound = UNNotificationSound.default()
content.categoryIdentifier = AppNotifications.category(.rejectedCallFromUnseenIdentity).identifier
let request = UNNotificationRequest.init(identifier: call.localId.uuidString, content: content, trigger: nil)
center.add(request)
}
}

View File

@ -8,9 +8,21 @@ import Foundation
* Strings re-used in multiple places should be added here.
*/
@objc class CallStrings: NSObject {
static let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'")
// MARK: Notification actions
static let callBackButtonTitle = NSLocalizedString("CALLBACK_BUTTON_TITLE", comment: "notification action")
static let confirmIdentityAndCallBackButtonTitle = NSLocalizedString("CONFIRM_IDENTITY_AND_CALLBACK_BUTTON_TITLE", comment: "notification action, confirming that it's OK to proceed calling after a caller's Safety Number has changed")
static let showThreadButtonTitle = NSLocalizedString("SHOW_THREAD_BUTTON_TITLE", comment: "notification action")
// MARK: Missed Call Notification
static let missedCallNotificationBody = NSLocalizedString("MISSED_CALL", comment: "notification title")
static let missedCallNotificationBodyWithCallerName = NSLocalizedString("MSGVIEW_MISSED_CALL_WITH_NAME", comment: "notification title. Embeds {{Caller's Name}}")
static let missedCallNotificationBodyWithoutCallerName = NSLocalizedString("MSGVIEW_MISSED_CALL_WITHOUT_NAME", comment: "notification title.")
static let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'")
// MARK: Missed with Unseen identity Notification
static let rejectedCallWithUnseenIdentityChangeNotificationBody = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY", comment: "notification action")
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITHOUT_CALLER_NAME", comment: "notification action")
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITH_CALLER_NAME", comment: "notification action")
}

View File

@ -84,6 +84,51 @@ NS_ASSUME_NONNULL_BEGIN
[contents addSection:[DebugUIContacts section]];
// After enqueing the notification you may want to background the app or lock the screen before it triggers, so we
// give a little delay.
uint64_t notificationDelay = 5;
[contents
addSection:[OWSTableSection
sectionWithTitle:[NSString stringWithFormat:@"Call Notifications (%llu second delay)",
notificationDelay]
items:@[
[OWSTableItem itemWithTitle:@"Missed Call"
actionBlock:^{
SignalCall *call = [SignalCall
incomingCallWithLocalId:[NSUUID new]
remotePhoneNumber:thread.contactIdentifier
signalingId:0];
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(notificationDelay * NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
[[Environment getCurrent]
.callService.notificationsAdapter
presentMissedCall:call
callerName:thread.name];
});
}],
[OWSTableItem
itemWithTitle:@"Rejected Call with Unseen Safety Number"
actionBlock:^{
SignalCall *call =
[SignalCall incomingCallWithLocalId:[NSUUID new]
remotePhoneNumber:thread.contactIdentifier
signalingId:0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(notificationDelay * NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
[[Environment getCurrent].callService.notificationsAdapter
presentRejectedCallWithUnseenIdentityChange:call
callerName:thread.name];
});
}],
]]];
DebugUITableViewController *viewController = [DebugUITableViewController new];
viewController.contents = contents;
[viewController presentFromViewController:fromViewController];

View File

@ -110,9 +110,10 @@ protocol CallServiceObserver: class {
private let accountManager: AccountManager
private let messageSender: MessageSender
private let contactsManager: OWSContactsManager
private let notificationsAdapter: CallNotificationsAdapter
private let storageManager: TSStorageManager
// Exposed by environment.m
internal let notificationsAdapter: CallNotificationsAdapter
internal var callUIAdapter: CallUIAdapter!
// MARK: Class
@ -200,6 +201,7 @@ protocol CallServiceObserver: class {
self.contactsManager = contactsManager
self.messageSender = messageSender
self.notificationsAdapter = notificationsAdapter
self.storageManager = TSStorageManager.shared()
super.init()
@ -465,9 +467,16 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
Logger.info("\(TAG) receivedCallOffer for thread:\(thread)")
let newCall = SignalCall.incomingCall(localId: UUID(), remotePhoneNumber: thread.contactIdentifier(), signalingId: callId)
guard call == nil else {
guard !self.storageManager.hasUnseenIdentityChange(forRecipientId: thread.contactIdentifier()) else {
let callerName = self.contactsManager.displayName(forPhoneIdentifier: thread.contactIdentifier())
self.notificationsAdapter.presentRejectedCallWithUnseenIdentityChange(newCall, callerName: callerName)
return
}
guard self.call == nil else {
// TODO on iOS10+ we can use CallKit to swap calls rather than just returning busy immediately.
Logger.verbose("\(TAG) receivedCallOffer for thread: \(thread) but we're already in call: \(call!)")

View File

@ -46,6 +46,8 @@ import Foundation
return false
}
// TODO possible to get here when. e.g. dialing from contacts/recent calls. Should verify seen latest SN.
// Check for microphone permissions
// Alternative way without prompting for permissions:
// if AVAudioSession.sharedInstance().recordPermission() == .denied {

View File

@ -1,5 +1,6 @@
// Created by Michael Kirk on 12/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -11,6 +12,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)presentMissedCall:(SignalCall *)call callerName:(NSString *)callerName;
- (void)presentRejectedCallWithUnseenIdentityChange:(SignalCall *)call
callerName:(NSString *)callerName
NS_SWIFT_NAME(presentRejectedCallWithUnseenIdentityChange(_:callerName:));
@end
NS_ASSUME_NONNULL_END

View File

@ -113,6 +113,43 @@
[self presentNotification:notification identifier:localCallId];
}
- (void)presentRejectedCallWithUnseenIdentityChange:(SignalCall *)call callerName:(NSString *)callerName
{
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:call.remotePhoneNumber];
OWSAssert(thread != nil);
UILocalNotification *notification = [UILocalNotification new];
notification.category = PushManagerCategoriesRejectedCallFromUnseenIdentityChange;
NSString *localCallId = call.localId.UUIDString;
notification.userInfo = @{
PushManagerUserInfoKeysLocalCallId : localCallId,
PushManagerUserInfoKeysCallBackSignalRecipientId : call.remotePhoneNumber,
Signal_Thread_UserInfo_Key : thread.uniqueId
};
NSString *alertMessage;
switch (self.notificationPreviewType) {
case NotificationNoNameNoPreview: {
alertMessage = [CallStrings rejectedCallWithUnseenIdentityChangeNotificationBody];
break;
}
case NotificationNameNoPreview:
case NotificationNamePreview: {
alertMessage = (([UIDevice currentDevice].supportsCallKit &&
[[Environment getCurrent].preferences isCallKitPrivacyEnabled])
? [CallStrings rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName]
: [NSString
stringWithFormat:[CallStrings
rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName],
callerName]);
break;
}
}
notification.alertBody = [NSString stringWithFormat:@"☎️ %@", alertMessage];
[self presentNotification:notification identifier:localCallId];
}
#pragma mark - Signal Messages
- (void)notifyUserForErrorMessage:(TSErrorMessage *)message inThread:(TSThread *)thread {

View File

@ -10,22 +10,25 @@ NS_ASSUME_NONNULL_BEGIN
@class UILocalNotification;
#define Signal_Thread_UserInfo_Key @"Signal_Thread_Id"
#define Signal_Message_UserInfo_Key @"Signal_Message_Id"
FOUNDATION_EXPORT NSString *const Signal_Thread_UserInfo_Key;
FOUNDATION_EXPORT NSString *const Signal_Message_UserInfo_Key;
#define Signal_Full_New_Message_Category @"Signal_Full_New_Message"
FOUNDATION_EXPORT NSString *const Signal_Full_New_Message_Category;
#define Signal_Message_Reply_Identifier @"Signal_New_Message_Reply"
#define Signal_Message_MarkAsRead_Identifier @"Signal_Message_MarkAsRead"
FOUNDATION_EXPORT NSString *const Signal_Message_Reply_Identifier;
FOUNDATION_EXPORT NSString *const Signal_Message_MarkAsRead_Identifier;
#pragma mark Signal Calls constants
FOUNDATION_EXPORT NSString *const PushManagerCategoriesIncomingCall;
FOUNDATION_EXPORT NSString *const PushManagerCategoriesMissedCall;
FOUNDATION_EXPORT NSString *const PushManagerCategoriesRejectedCallFromUnseenIdentityChange;
FOUNDATION_EXPORT NSString *const PushManagerActionsAcceptCall;
FOUNDATION_EXPORT NSString *const PushManagerActionsDeclineCall;
FOUNDATION_EXPORT NSString *const PushManagerActionsCallBack;
FOUNDATION_EXPORT NSString *const PushManagerActionsConfirmIdentityAndCallBack;
FOUNDATION_EXPORT NSString *const PushManagerActionsShowThread;
FOUNDATION_EXPORT NSString *const PushManagerUserInfoKeysCallBackSignalRecipientId;
FOUNDATION_EXPORT NSString *const PushManagerUserInfoKeysLocalCallId;

View File

@ -16,7 +16,13 @@
#import <SignalServiceKit/TSOutgoingMessage.h>
#import <SignalServiceKit/TSSocketManager.h>
#define pushManagerDomain @"org.whispersystems.pushmanager"
NSString *const Signal_Thread_UserInfo_Key = @"Signal_Thread_Id";
NSString *const Signal_Message_UserInfo_Key = @"Signal_Message_Id";
NSString *const Signal_Full_New_Message_Category = @"Signal_Full_New_Message";
NSString *const Signal_Message_Reply_Identifier = @"Signal_New_Message_Reply";
NSString *const Signal_Message_MarkAsRead_Identifier = @"Signal_Message_MarkAsRead";
@interface PushManager ()
@ -132,10 +138,9 @@
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)())completionHandler
{
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
DDLogInfo(@"%@ handling action with identifier: %@", self.tag, identifier);
if ([identifier isEqualToString:Signal_Message_Reply_Identifier]) {
DDLogInfo(@"%@ received reply identifier", self.tag);
NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key];
if (threadId) {
@ -146,6 +151,7 @@
messageBody:responseInfo[UIUserNotificationActionResponseTypedTextKey]];
[self.messageSender sendMessage:message
success:^{
// TODO do we really want to mark them all as read?
[self markAllInThreadAsRead:notification.userInfo completionHandler:completionHandler];
[[[[Environment getCurrent] signalsViewController] tableView] reloadData];
}
@ -162,10 +168,9 @@
}];
}
} else if ([identifier isEqualToString:Signal_Message_MarkAsRead_Identifier]) {
// TODO mark all as read? Or just this one?
[self markAllInThreadAsRead:notification.userInfo completionHandler:completionHandler];
} else if ([identifier isEqualToString:PushManagerActionsAcceptCall]) {
DDLogInfo(@"%@ received accept call action", self.tag);
NSString *localIdString = notification.userInfo[PushManagerUserInfoKeysLocalCallId];
if (!localIdString) {
DDLogError(@"%@ missing localIdString.", self.tag);
@ -178,11 +183,9 @@
return;
}
[self.callUIAdapter answerCallWithLocalId:localId];
completionHandler();
} else if ([identifier isEqualToString:PushManagerActionsDeclineCall]) {
DDLogInfo(@"%@ received decline call action", self.tag);
NSString *localIdString = notification.userInfo[PushManagerUserInfoKeysLocalCallId];
if (!localIdString) {
DDLogError(@"%@ missing localIdString.", self.tag);
@ -196,9 +199,8 @@
}
[self.callUIAdapter declineCallWithLocalId:localId];
completionHandler();
} else if ([identifier isEqualToString:PushManagerActionsCallBack]) {
DDLogInfo(@"%@ received call back action", self.tag);
NSString *recipientId = notification.userInfo[PushManagerUserInfoKeysCallBackSignalRecipientId];
if (!recipientId) {
DDLogError(@"%@ missing call back id", self.tag);
@ -206,9 +208,26 @@
}
[self.callUIAdapter startAndShowOutgoingCallWithRecipientId:recipientId];
} else {
DDLogDebug(@"%@ Unhandled action with identifier: %@", self.tag, identifier);
completionHandler();
} else if ([identifier isEqualToString:PushManagerActionsConfirmIdentityAndCallBack]) {
NSString *recipientId = notification.userInfo[PushManagerUserInfoKeysCallBackSignalRecipientId];
if (!recipientId) {
DDLogError(@"%@ missing call back id", self.tag);
return;
}
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
[MarkIdentityAsSeenJob runWithThread:thread];
[self.callUIAdapter startAndShowOutgoingCallWithRecipientId:recipientId];
completionHandler();
} else if ([identifier isEqualToString:PushManagerActionsShowThread]) {
NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
completionHandler();
} else {
DDLogError(@"%@ Unhandled action with identifier: %@", self.tag, identifier);
OWSFail(@"Unhandled action");
NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
completionHandler();
@ -338,10 +357,14 @@
NSString *const PushManagerCategoriesIncomingCall = @"PushManagerCategoriesIncomingCall";
NSString *const PushManagerCategoriesMissedCall = @"PushManagerCategoriesMissedCall";
NSString *const PushManagerCategoriesRejectedCallFromUnseenIdentityChange =
@"PushManagerCategoriesRejectedCallFromUnseenIdentityChange";
NSString *const PushManagerActionsAcceptCall = @"PushManagerActionsAcceptCall";
NSString *const PushManagerActionsDeclineCall = @"PushManagerActionsDeclineCall";
NSString *const PushManagerActionsCallBack = @"PushManagerActionsCallBack";
NSString *const PushManagerActionsConfirmIdentityAndCallBack = @"PushManagerActionsConfirmIdentityAndCallBack";
NSString *const PushManagerActionsShowThread = @"PushManagerActionsShowThread";
NSString *const PushManagerUserInfoKeysLocalCallId = @"PushManagerUserInfoKeysLocalCallId";
NSString *const PushManagerUserInfoKeysCallBackSignalRecipientId = @"PushManagerUserInfoKeysCallBackSignalRecipientId";
@ -379,12 +402,38 @@ NSString *const PushManagerUserInfoKeysCallBackSignalRecipientId = @"PushManager
callBackAction.destructive = NO;
callBackAction.authenticationRequired = YES;
UIMutableUserNotificationCategory *callCategory = [UIMutableUserNotificationCategory new];
callCategory.identifier = PushManagerCategoriesMissedCall;
[callCategory setActions:@[ callBackAction ] forContext:UIUserNotificationActionContextMinimal];
[callCategory setActions:@[ callBackAction ] forContext:UIUserNotificationActionContextDefault];
UIMutableUserNotificationCategory *missedCallCategory = [UIMutableUserNotificationCategory new];
missedCallCategory.identifier = PushManagerCategoriesMissedCall;
[missedCallCategory setActions:@[ callBackAction ] forContext:UIUserNotificationActionContextMinimal];
[missedCallCategory setActions:@[ callBackAction ] forContext:UIUserNotificationActionContextDefault];
return callCategory;
return missedCallCategory;
}
- (UIUserNotificationCategory *)signalRejectedCallWithUnseenIdentityChangeCategory
{
UIMutableUserNotificationAction *confirmAndCallBackAction = [UIMutableUserNotificationAction new];
confirmAndCallBackAction.identifier = PushManagerActionsConfirmIdentityAndCallBack;
confirmAndCallBackAction.title = [CallStrings confirmIdentityAndCallBackButtonTitle];
confirmAndCallBackAction.activationMode = UIUserNotificationActivationModeForeground;
confirmAndCallBackAction.destructive = NO;
confirmAndCallBackAction.authenticationRequired = YES;
UIMutableUserNotificationAction *showThreadAction = [UIMutableUserNotificationAction new];
showThreadAction.identifier = PushManagerActionsShowThread;
showThreadAction.title = [CallStrings showThreadButtonTitle];
showThreadAction.activationMode = UIUserNotificationActivationModeForeground;
showThreadAction.destructive = NO;
showThreadAction.authenticationRequired = YES;
UIMutableUserNotificationCategory *rejectedCallCategory = [UIMutableUserNotificationCategory new];
rejectedCallCategory.identifier = PushManagerCategoriesRejectedCallFromUnseenIdentityChange;
[rejectedCallCategory setActions:@[ confirmAndCallBackAction, showThreadAction ]
forContext:UIUserNotificationActionContextMinimal];
[rejectedCallCategory setActions:@[ confirmAndCallBackAction, showThreadAction ]
forContext:UIUserNotificationActionContextDefault];
return rejectedCallCategory;
}
#pragma mark Util
@ -408,6 +457,7 @@ NSString *const PushManagerUserInfoKeysCallBackSignalRecipientId = @"PushManager
categories:[NSSet setWithObjects:[self fullNewMessageNotificationCategory],
[self signalIncomingCallCategory],
[self signalMissedCallCategory],
[self signalRejectedCallWithUnseenIdentityChangeCategory],
nil]];
[UIApplication.sharedApplication registerUserNotificationSettings:settings];

View File

@ -212,7 +212,7 @@
"CALL_VIEW_SETTINGS_NAG_SHOW_CALL_SETTINGS" = "Show Privacy Settings";
/* notification action */
"CALLBACK_BUTTON_TITLE" = "Call back";
"CALLBACK_BUTTON_TITLE" = "Call Back";
/* The generic name used for calls if CallKit privacy is enabled */
"CALLKIT_ANONYMOUS_CONTACT_NAME" = "Signal User";
@ -232,6 +232,9 @@
/* No comment provided by engineer. */
"CONFIRM_ACCOUNT_DESTRUCTION_TITLE" = "Are you sure you want to delete your account?";
/* notification action, confirming that it's OK to proceed calling after a caller's Safety Number has changed */
"CONFIRM_IDENTITY_AND_CALLBACK_BUTTON_TITLE" = "Confirm and Call Back";
/* Alert body */
"CONFIRM_LEAVE_GROUP_DESCRIPTION" = "You will no longer be able to send or receive messages in this group.";
@ -670,9 +673,6 @@
/* table cell label in conversation settings */
"LIST_GROUP_MEMBERS_ACTION" = "List Group Members";
/* No comment provided by engineer. */
"load_earlier_messages" = "load_earlier_messages";
/* No comment provided by engineer. */
"LOGGING_SECTION" = "Logging";
@ -736,6 +736,15 @@
/* notification title */
"MISSED_CALL" = "Missed call";
/* notification action */
"MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY" = "Missed call because the caller's Safety Number changed.";
/* notification action */
"MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITH_CALLER_NAME" = "Missed call from %@ because their Safety Number changed.";
/* notification action */
"MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITHOUT_CALLER_NAME" = "Missed call because the caller's Safety Number changed.";
/* Alert body
Alert body when camera is not authorized */
"MISSING_CAMERA_PERMISSION_MESSAGE" = "Signal needs access to your camera for video calls. You can grant this permission in the Settings app >> Privacy >> Camera >> Signal";
@ -950,7 +959,7 @@
"PROCEED_BUTTON" = "Proceed";
/* No comment provided by engineer. */
"PUSH_MANAGER_MARKREAD" = "Mark as read";
"PUSH_MANAGER_MARKREAD" = "Mark as Read";
/* No comment provided by engineer. */
"PUSH_MANAGER_REPLY" = "Reply";
@ -1258,6 +1267,9 @@
/* Action sheet item */
"SHOW_SAFETY_NUMBER_ACTION" = "Show new safety number";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Thread";
/* {{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{1 day}}'. See other *_TIME_AMOUNT strings */
"SINGLE_DAY_TIME_AMOUNT" = "%u day";