Place Signal/Redphone calls from system contacts

// FREEBIE
This commit is contained in:
Michael Kirk 2017-02-01 17:49:32 -05:00
parent 724a1c9b20
commit bbfd9ba74d
7 changed files with 161 additions and 8 deletions

View File

@ -28,6 +28,8 @@
451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; };
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; };
452C46901E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; };
452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */; };
452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
@ -62,6 +64,7 @@
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; };
45847E871E4283C30080EAB3 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45847E861E4283C30080EAB3 /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
458967111DC117CC00E9DD21 /* AccountManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458967101DC117CC00E9DD21 /* AccountManagerTest.swift */; };
@ -620,6 +623,7 @@
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; };
4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageEditing.h; sourceTree = "<group>"; };
452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = "<group>"; };
452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MesssagesBubblesSizeCalculatorTest.swift; path = Models/MesssagesBubblesSizeCalculatorTest.swift; sourceTree = "<group>"; };
452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsTableViewController.h; sourceTree = "<group>"; };
452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSConversationSettingsTableViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
@ -656,6 +660,7 @@
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = "<group>"; };
45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = "<group>"; };
45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = "<group>"; };
45847E861E4283C30080EAB3 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactAvatarBuilder.h; sourceTree = "<group>"; };
45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactAvatarBuilder.m; sourceTree = "<group>"; };
4589670F1DC117CC00E9DD21 /* SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
@ -1242,6 +1247,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
45847E871E4283C30080EAB3 /* Intents.framework in Frameworks */,
4509E79A1DD653700025A59F /* WebRTC.framework in Frameworks */,
456C38961DC7B882007536A7 /* PromiseKit.framework in Frameworks */,
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */,
@ -1555,6 +1561,7 @@
4574A5D51DD6704700C6B692 /* CallService.swift */,
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */,
45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */,
452C468E1E427E200087B011 /* OutboundCallInitiator.swift */,
);
path = call;
sourceTree = "<group>";
@ -2430,6 +2437,7 @@
D221A08C169C9E5E00537ABF /* Frameworks */ = {
isa = PBXGroup;
children = (
45847E861E4283C30080EAB3 /* Intents.framework */,
45BD60811DE9547E00A8F436 /* Contacts.framework */,
4509E7991DD653700025A59F /* WebRTC.framework */,
451DE9F11DC1585F00810E42 /* PromiseKit.framework */,
@ -3199,6 +3207,7 @@
B66B9F721AEA6D1100E2E609 /* NotificationSettingsViewController.m in Sources */,
76EB059018170B33006006FC /* IgnoredPacketFailure.m in Sources */,
76EB05D418170B33006006FC /* ZrtpManager.m in Sources */,
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
76EB058E18170B33006006FC /* HostNameEndPoint.m in Sources */,
E19167A418A9687800B7A468 /* DH3KKeyAgreementParticipant.m in Sources */,
E16E5BF018AAC40200B7C403 /* EvpKeyAgreement.m in Sources */,
@ -3309,6 +3318,7 @@
B660F7131C29988E00687D6E /* SoundPlayer.m in Sources */,
B660F7141C29988E00687D6E /* RecentCall.m in Sources */,
B660F7151C29988E00687D6E /* RecentCallManager.m in Sources */,
452C46901E427E200087B011 /* OutboundCallInitiator.swift in Sources */,
451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */,
B660F7161C29988E00687D6E /* GroupContactsResult.m in Sources */,
B660F7171C29988E00687D6E /* OWSContactsManager.m in Sources */,

View File

@ -31,6 +31,7 @@
#import <SignalServiceKit/TSAccountManager.h>
@import WebRTC;
@import Intents;
NSString *const AppDelegateStoryboardMain = @"Main";
NSString *const AppDelegateStoryboardRegistration = @"Registration";
@ -333,10 +334,52 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) {
DDLogInfo(@"%@ got start video call intent", self.tag);
[[Environment getCurrent].callService handleCallKitStartVideo];
} else if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) {
// TODO guard if less than iOS10.
DDLogInfo(@"%@ got start audio call intent", self.tag);
// hoooooooooooly moly.
INInteraction *interaction = [userActivity interaction];
INIntent *intent = interaction.intent;
if (![intent isKindOfClass:[INStartAudioCallIntent class]]) {
DDLogError(@"%@ unexpected class for start call audio: %@", self.tag, intent);
return NO;
}
INStartAudioCallIntent *startCallIntent = (INStartAudioCallIntent *)intent;
NSString *_Nullable handle = startCallIntent.contacts.firstObject.personHandle.value;
if (!handle) {
DDLogWarn(@"%@ unable to find handle in startCallIntent: %@", self.tag, startCallIntent);
return NO;
}
CallUIAdapter *callUIAdapter = [Environment getCurrent].callService.callUIAdapter;
OWSAssert(callUIAdapter);
ContactsUpdater *contactsUpdater = [Environment getCurrent].contactsUpdater;
OWSAssert(contactsUpdater);
OWSContactsManager *contactsManager = [Environment getCurrent].contactsManager;
OWSAssert(contactsManager);
PhoneManager *phoneManager = [Environment getCurrent].phoneManager;
OWSAssert(phoneManager);
OutboundCallInitiator *outboundCallInitiator =
[[OutboundCallInitiator alloc] initWithRedphoneManager:phoneManager
callUIAdapter:callUIAdapter
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
return [outboundCallInitiator initiateCallWithHandle:handle];
} else {
DDLogWarn(
@"%@ called %s with userActivity: %@, but not yet supported.", self.tag, __PRETTY_FUNCTION__, userActivity);
DDLogWarn(@"%@ called %s with userActivity: %@, but not yet supported.",
self.tag,
__PRETTY_FUNCTION__,
userActivity.activityType);
}
// TODO Something like...

View File

@ -16,7 +16,7 @@
#import "OWSError.h"
#import "OWSLogger.h"
#import "OWSWebRTCDataProtos.pb.h"
#import "PhoneNumber.h"
#import "PhoneManager.h"
#import "PropertyListPreferences.h"
#import "PureLayout.h"
#import "PushManager.h"
@ -28,6 +28,7 @@
#import "UIView+OWS.h"
#import <JSQSystemSoundPlayer.h>
#import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/Cryptography.h>
#import <SignalServiceKit/NSData+Base64.h>
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
@ -45,6 +46,7 @@
#import <SignalServiceKit/OWSOutgoingCallMessage.h>
#import <SignalServiceKit/OWSSignalService.h>
#import <SignalServiceKit/OWSTurnServerInfoRequest.h>
#import <SignalServiceKit/PhoneNumber.h>
#import <SignalServiceKit/SignalRecipient.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSCall.h>

View File

@ -0,0 +1,94 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
/**
* Creates an outbound call via either Redphone or WebRTC depending on participant preferences.
*/
@objc class OutboundCallInitiator: NSObject {
let TAG = "[OutboundCallInitiator]"
let callUIAdapter: CallUIAdapter
let redphoneManager: PhoneManager
let contactsManager: OWSContactsManager
let contactsUpdater: ContactsUpdater
init(redphoneManager: PhoneManager, callUIAdapter: CallUIAdapter, contactsManager: OWSContactsManager, contactsUpdater: ContactsUpdater) {
self.redphoneManager = redphoneManager
self.callUIAdapter = callUIAdapter
self.contactsManager = contactsManager
self.contactsUpdater = contactsUpdater
}
/**
* |handle| is a user formatted phone number, e.g. from a system contacts entry
*/
public func initiateCall(handle: String) -> Bool {
Logger.info("\(TAG) in \(#function) with handle: \(handle)")
guard let recipientId = PhoneNumber(fromUserSpecifiedText: handle).toE164() else {
Logger.warn("\(TAG) unable to parse signalId from phone number: \(handle)")
return false
}
return initiateCall(recipientId: recipientId)
}
/**
* |recipientId| is a e164 formatted phone number.
*/
public func initiateCall(recipientId: String) -> Bool {
let localWantsWebRTC = Environment.preferences().isWebRTCEnabled()
if !localWantsWebRTC {
return self.initiateRedphoneCall(recipientId: recipientId)
}
// Since users can toggle this setting, which is only communicated during contact sync, it's easy to imagine the
// preference getting stale. Especially as users are toggling the feature to test calls. So here, we opt for a
// blocking network request *every* time we place a call to make sure we've got up to date preferences.
//
// e.g. The following would suffice if we weren't worried about stale preferences.
// SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:self.thread.contactIdentifier];
self.contactsUpdater.lookupIdentifier(recipientId,
success: { recipient in
let remoteWantsWebRTC = recipient.supportsWebRTC
Logger.debug("\(self.TAG) localWantsWebRTC: \(localWantsWebRTC), remoteWantsWebRTC: \(remoteWantsWebRTC)")
if localWantsWebRTC, remoteWantsWebRTC {
_ = self.initiateWebRTCAudioCall(recipientId: recipientId)
} else {
_ = self.initiateRedphoneCall(recipientId: recipientId)
}
},
failure: { error in
Logger.warn("\(self.TAG) looking up recipientId: \(recipientId) failed with error \(error)")
// TODO fail with alert. e.g. when someone tries to call a non signal user from their contacts we should inform them.
})
// Since we've already dispatched async to make sure we have fresh webrtc preference data
// we don't have a meaningful value to return here - but we're not using it anway. =/
return true
}
private func initiateRedphoneCall(recipientId: String) -> Bool {
Logger.info("\(TAG) Placing redphone call to: \(recipientId)")
let number = PhoneNumber.tryParsePhoneNumber(fromUserSpecifiedText: recipientId)
let contact = self.contactsManager.latestContact(for: number)
assert(number != nil)
assert(contact != nil)
redphoneManager.initiateOutgoingCall(to: contact, atRemoteNumber: number)
return true
}
private func initiateWebRTCAudioCall(recipientId: String) -> Bool {
callUIAdapter.callBack(recipientId: recipientId)
return true
}
}

View File

@ -1,3 +1,7 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Logging.h"
#import "PacketHandler.h"

View File

@ -1,3 +1,7 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "Environment.h"
#import "Constraints.h"
#import "DH3KKeyAgreementProtocol.h"

View File

@ -1,9 +1,5 @@
//
// MessagesViewController.m
// Signal
//
// Created by Dylan Bourgeois on 28/10/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "AppDelegate.h"