users can opt out of CallKit

// FREEBIE
pull/1/head
Michael Kirk 8 years ago
parent d8df4b9e35
commit 2a9aa4c852

@ -204,6 +204,7 @@ import Foundation
} }
private func setAudioSession(category: String) { private func setAudioSession(category: String) {
// FIXME Why this default mode? It doesn't work with e.g. "SoloAmbient", causing `AVAudioSession.sharedInstance().setCategory(category, mode: mode, options: options)` to err
setAudioSession(category:category, setAudioSession(category:category,
mode:AVAudioSessionModeVoiceChat, mode:AVAudioSessionModeVoiceChat,
options:AVAudioSessionCategoryOptions(rawValue: 0)) options:AVAudioSessionCategoryOptions(rawValue: 0))

@ -100,9 +100,13 @@ protocol CallServiceObserver: class {
// MARK: Dependencies // MARK: Dependencies
let accountManager: AccountManager private let accountManager: AccountManager
let messageSender: MessageSender private let messageSender: MessageSender
var callUIAdapter: CallUIAdapter! private let contactsManager: OWSContactsManager
private let notificationsAdapter: CallNotificationsAdapter
// Exposed by environment.m
internal var callUIAdapter: CallUIAdapter!
// MARK: Class // MARK: Class
@ -179,11 +183,13 @@ protocol CallServiceObserver: class {
required init(accountManager: AccountManager, contactsManager: OWSContactsManager, messageSender: MessageSender, notificationsAdapter: CallNotificationsAdapter) { required init(accountManager: AccountManager, contactsManager: OWSContactsManager, messageSender: MessageSender, notificationsAdapter: CallNotificationsAdapter) {
self.accountManager = accountManager self.accountManager = accountManager
self.contactsManager = contactsManager
self.messageSender = messageSender self.messageSender = messageSender
self.notificationsAdapter = notificationsAdapter
super.init() super.init()
self.callUIAdapter = CallUIAdapter(callService: self, contactsManager: contactsManager, notificationsAdapter: notificationsAdapter) self.createCallUIAdapter()
NotificationCenter.default.addObserver(self, NotificationCenter.default.addObserver(self,
selector:#selector(didEnterBackground), selector:#selector(didEnterBackground),
@ -213,6 +219,16 @@ protocol CallServiceObserver: class {
self.updateIsVideoEnabled() self.updateIsVideoEnabled()
} }
public func createCallUIAdapter() {
AssertIsOnMainThread()
if self.call != nil {
Logger.warn("\(TAG) ending current call in \(#function). Did user toggle callkit preference while in a call?")
self.terminateCall()
}
self.callUIAdapter = CallUIAdapter(callService: self, contactsManager: self.contactsManager, notificationsAdapter: self.notificationsAdapter)
}
// MARK: - Class Methods // MARK: - Class Methods
// MARK: Notifications // MARK: Notifications

@ -10,14 +10,12 @@ import Foundation
@objc class OutboundCallInitiator: NSObject { @objc class OutboundCallInitiator: NSObject {
let TAG = "[OutboundCallInitiator]" let TAG = "[OutboundCallInitiator]"
let callUIAdapter: CallUIAdapter
let redphoneManager: PhoneManager let redphoneManager: PhoneManager
let contactsManager: OWSContactsManager let contactsManager: OWSContactsManager
let contactsUpdater: ContactsUpdater let contactsUpdater: ContactsUpdater
init(redphoneManager: PhoneManager, callUIAdapter: CallUIAdapter, contactsManager: OWSContactsManager, contactsUpdater: ContactsUpdater) { init(redphoneManager: PhoneManager, contactsManager: OWSContactsManager, contactsUpdater: ContactsUpdater) {
self.redphoneManager = redphoneManager self.redphoneManager = redphoneManager
self.callUIAdapter = callUIAdapter
self.contactsManager = contactsManager self.contactsManager = contactsManager
self.contactsUpdater = contactsUpdater self.contactsUpdater = contactsUpdater
@ -93,6 +91,14 @@ import Foundation
} }
private func initiateWebRTCAudioCall(recipientId: String) -> Bool { private func initiateWebRTCAudioCall(recipientId: String) -> Bool {
// Rather than an init-assigned dependency property, we access `callUIAdapter` via Environment
// because it can change after app launch due to user settings
guard let callUIAdapter = Environment.getCurrent().callUIAdapter else {
assertionFailure()
Logger.error("\(TAG) can't initiate call because callUIAdapter is nil")
return false
}
callUIAdapter.startAndShowOutgoingCall(recipientId: recipientId) callUIAdapter.startAndShowOutgoingCall(recipientId: recipientId)
return true return true
} }

@ -77,11 +77,11 @@ extension CallUIAdaptee {
// So we use the non-CallKit call UI. // So we use the non-CallKit call UI.
Logger.info("\(TAG) choosing non-callkit adaptee for simulator.") Logger.info("\(TAG) choosing non-callkit adaptee for simulator.")
adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter) adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
} else if #available(iOS 10.0, *) { } else if #available(iOS 10.0, *), Environment.getCurrent().preferences.isCallKitEnabled() {
Logger.info("\(TAG) choosing callkit adaptee for iOS10+") Logger.info("\(TAG) choosing callkit adaptee for iOS10+")
adaptee = CallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter) adaptee = CallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
} else { } else {
Logger.info("\(TAG) choosing non-callkit adaptee for older iOS") Logger.info("\(TAG) choosing non-callkit adaptee")
adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter) adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
} }

@ -183,11 +183,9 @@ static Environment *environment = nil;
@synchronized (self) { @synchronized (self) {
if (!_outboundCallInitiator) { if (!_outboundCallInitiator) {
OWSAssert(self.phoneManager); OWSAssert(self.phoneManager);
OWSAssert(self.callUIAdapter);
OWSAssert(self.contactsManager); OWSAssert(self.contactsManager);
OWSAssert(self.contactsUpdater); OWSAssert(self.contactsUpdater);
_outboundCallInitiator = [[OutboundCallInitiator alloc] initWithRedphoneManager:self.phoneManager _outboundCallInitiator = [[OutboundCallInitiator alloc] initWithRedphoneManager:self.phoneManager
callUIAdapter:self.callUIAdapter
contactsManager:self.contactsManager contactsManager:self.contactsManager
contactsUpdater:self.contactsUpdater]; contactsUpdater:self.contactsUpdater];
} }

@ -64,9 +64,18 @@ extern NSString *const PropertyListPreferencesKeyEnableDebugLog;
- (nullable NSString *)lastRanVersion; - (nullable NSString *)lastRanVersion;
- (NSString *)setAndGetCurrentVersion; - (NSString *)setAndGetCurrentVersion;
#pragma mark - Calling
#pragma mark WebRTC
- (BOOL)isWebRTCEnabled; - (BOOL)isWebRTCEnabled;
- (void)setIsWebRTCEnabled:(BOOL)flag; - (void)setIsWebRTCEnabled:(BOOL)flag;
#pragma mark Callkit
- (BOOL)isCallKitEnabled;
- (void)setIsCallKitEnabled:(BOOL)flag;
#pragma mark - Block on Identity Change #pragma mark - Block on Identity Change
- (BOOL)shouldBlockOnIdentityChange; - (BOOL)shouldBlockOnIdentityChange;

@ -24,6 +24,7 @@ NSString *const PropertyListPreferencesKeyHasRegisteredVoipPush = @"VOIPPushEnab
NSString *const PropertyListPreferencesKeyLastRecordedPushToken = @"LastRecordedPushToken"; NSString *const PropertyListPreferencesKeyLastRecordedPushToken = @"LastRecordedPushToken";
NSString *const PropertyListPreferencesKeyLastRecordedVoipToken = @"LastRecordedVoipToken"; NSString *const PropertyListPreferencesKeyLastRecordedVoipToken = @"LastRecordedVoipToken";
NSString *const PropertyListPreferencesKeyWebRTCEnabled = @"WebRTCEnabled"; NSString *const PropertyListPreferencesKeyWebRTCEnabled = @"WebRTCEnabled";
NSString *const PropertyListPreferencesKeyCallKitEnabled = @"CallKitEnabled";
@implementation PropertyListPreferences @implementation PropertyListPreferences
@ -79,13 +80,6 @@ NSString *const PropertyListPreferencesKeyWebRTCEnabled = @"WebRTCEnabled";
return preference ? [preference boolValue] : YES; return preference ? [preference boolValue] : YES;
} }
- (BOOL)isWebRTCEnabled
{
NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyWebRTCEnabled];
// Currently default to NO.
return preference ? [preference boolValue] : NO;
}
- (BOOL)getHasSentAMessage - (BOOL)getHasSentAMessage
{ {
NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyHasSentAMessage]; NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyHasSentAMessage];
@ -127,10 +121,6 @@ NSString *const PropertyListPreferencesKeyWebRTCEnabled = @"WebRTCEnabled";
[self setValueForKey:PropertyListPreferencesKeyScreenSecurity toValue:@(flag)]; [self setValueForKey:PropertyListPreferencesKeyScreenSecurity toValue:@(flag)];
} }
- (void)setIsWebRTCEnabled:(BOOL)flag
{
[self setValueForKey:PropertyListPreferencesKeyWebRTCEnabled toValue:@(flag)];
}
- (void)setHasRegisteredVOIPPush:(BOOL)enabled - (void)setHasRegisteredVOIPPush:(BOOL)enabled
{ {
@ -179,6 +169,35 @@ NSString *const PropertyListPreferencesKeyWebRTCEnabled = @"WebRTCEnabled";
return currentVersion; return currentVersion;
} }
#pragma mark - Calling
#pragma mark WebRTC
- (BOOL)isWebRTCEnabled
{
NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyWebRTCEnabled];
// Currently default to NO.
return preference ? [preference boolValue] : NO;
}
- (void)setIsWebRTCEnabled:(BOOL)flag
{
[self setValueForKey:PropertyListPreferencesKeyWebRTCEnabled toValue:@(flag)];
}
#pragma mark CallKit
- (BOOL)isCallKitEnabled
{
NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyCallKitEnabled];
return preference ? [preference boolValue] : YES;
}
- (void)setIsCallKitEnabled:(BOOL)flag
{
[self setValueForKey:PropertyListPreferencesKeyCallKitEnabled toValue:@(flag)];
}
#pragma mark Notification Preferences #pragma mark Notification Preferences
- (BOOL)soundInForeground - (BOOL)soundInForeground

@ -17,18 +17,26 @@ NS_ASSUME_NONNULL_BEGIN
@interface AdvancedSettingsTableViewController () @interface AdvancedSettingsTableViewController ()
@property NSArray *sectionsArray;
@property (nonatomic) UITableViewCell *enableWebRTCCell; @property (nonatomic) UITableViewCell *enableWebRTCCell;
@property (nonatomic) UITableViewCell *enableCallKitCell;
@property (nonatomic) UITableViewCell *enableLogCell; @property (nonatomic) UITableViewCell *enableLogCell;
@property (nonatomic) UITableViewCell *submitLogCell; @property (nonatomic) UITableViewCell *submitLogCell;
@property (nonatomic) UITableViewCell *registerPushCell; @property (nonatomic) UITableViewCell *registerPushCell;
@property (nonatomic) UISwitch *enableWebRTCSwitch; @property (nonatomic) UISwitch *enableWebRTCSwitch;
@property (nonatomic) UISwitch *enableCallKitSwitch;
@property (nonatomic) UISwitch *enableLogSwitch; @property (nonatomic) UISwitch *enableLogSwitch;
@property (nonatomic, readonly) BOOL supportsCallKit;
@end @end
typedef NS_ENUM(NSInteger, AdvancedSettingsTableViewControllerSection) {
AdvancedSettingsTableViewControllerSectionLogging,
AdvancedSettingsTableViewControllerSectionCalling,
AdvancedSettingsTableViewControllerSectionPushNotifications,
AdvancedSettingsTableViewControllerSection_Count // meta section
};
@implementation AdvancedSettingsTableViewController @implementation AdvancedSettingsTableViewController
- (void)viewDidLoad { - (void)viewDidLoad {
@ -37,16 +45,13 @@ NS_ASSUME_NONNULL_BEGIN
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
} }
- (instancetype)init { - (instancetype)init
self.sectionsArray = @[ {
NSLocalizedString(@"LOGGING_SECTION", nil),
NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts")
];
return [super initWithStyle:UITableViewStyleGrouped]; return [super initWithStyle:UITableViewStyleGrouped];
} }
- (void)loadView { - (void)loadView
{
[super loadView]; [super loadView];
self.title = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE", @""); self.title = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE", @"");
@ -63,6 +68,16 @@ NS_ASSUME_NONNULL_BEGIN
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
self.enableWebRTCCell.accessoryView = self.enableWebRTCSwitch; self.enableWebRTCCell.accessoryView = self.enableWebRTCSwitch;
// CallKit opt-out
self.enableCallKitCell = [UITableViewCell new];
self.enableCallKitCell.textLabel.text = NSLocalizedString(@"SETTINGS_ADVANCED_CALLKIT_TITLE", @"Short table cell label");
self.enableCallKitSwitch = [UISwitch new];
[self.enableCallKitSwitch setOn:[[Environment getCurrent].preferences isCallKitEnabled]];
[self.enableCallKitSwitch addTarget:self
action:@selector(didToggleEnableCallKitSwitch:)
forControlEvents:UIControlEventTouchUpInside];
self.enableCallKitCell.accessoryView = self.enableCallKitSwitch;
// Enable Log // Enable Log
self.enableLogCell = [[UITableViewCell alloc] init]; self.enableLogCell = [[UITableViewCell alloc] init];
self.enableLogCell.textLabel.text = NSLocalizedString(@"SETTINGS_ADVANCED_DEBUGLOG", @""); self.enableLogCell.textLabel.text = NSLocalizedString(@"SETTINGS_ADVANCED_DEBUGLOG", @"");
@ -85,14 +100,18 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Table view data source #pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return (NSInteger)[self.sectionsArray count]; return AdvancedSettingsTableViewControllerSection_Count;
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case 0: AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)section;
return 1 + (self.enableLogSwitch.isOn ? 2 : 1); switch (settingsSection) {
case 1: case AdvancedSettingsTableViewControllerSectionLogging:
return self.enableLogSwitch.isOn ? 2 : 1;
case AdvancedSettingsTableViewControllerSectionCalling:
return self.supportsCallKit ? 2 : 1;
case AdvancedSettingsTableViewControllerSectionPushNotifications:
return 1; return 1;
default: default:
return 0; return 0;
@ -101,26 +120,61 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{ {
return [self.sectionsArray objectAtIndex:(NSUInteger)section]; AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)section;
switch (settingsSection) {
case AdvancedSettingsTableViewControllerSectionLogging:
return NSLocalizedString(@"LOGGING_SECTION", nil);
case AdvancedSettingsTableViewControllerSectionCalling:
return NSLocalizedString(@"SETTINGS_SECTION_TITLE_CALLING", @"settings topic header for table section");
case AdvancedSettingsTableViewControllerSectionPushNotifications:
return NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts");
default:
return 0;
}
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
if (indexPath.section == 0) { {
switch (indexPath.row) { AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)section;
case 0: switch (settingsSection) {
return self.enableWebRTCCell; case AdvancedSettingsTableViewControllerSectionCalling:
case 1: return NSLocalizedString(@"SETTINGS_SECTION_CALL_KIT_DESCRIPTION", @"Settings table section footer.");
return self.enableLogCell; default:
case 2: return nil;
return self.enableLogSwitch.isOn ? self.submitLogCell : self.registerPushCell;
}
} else {
return self.registerPushCell;
} }
}
NSAssert(false, @"No Cell configured"); - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil; AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)indexPath.section;
switch (settingsSection) {
case AdvancedSettingsTableViewControllerSectionLogging:
switch (indexPath.row) {
case 0:
return self.enableLogCell;
case 1:
OWSAssert(self.enableLogSwitch.isOn);
return self.submitLogCell;
}
case AdvancedSettingsTableViewControllerSectionCalling:
switch (indexPath.row) {
case 0:
return self.enableWebRTCCell;
case 1:
OWSAssert(self.supportsCallKit);
return self.enableCallKitCell;
default:
// Unknown cell
OWSAssert(NO);
return nil;
}
case AdvancedSettingsTableViewControllerSectionPushNotifications:
return self.registerPushCell;
default:
// Unknown section
OWSAssert(NO);
return nil;
}
} }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
@ -207,6 +261,19 @@ NS_ASSUME_NONNULL_BEGIN
[self.tableView reloadData]; [self.tableView reloadData];
} }
- (void)didToggleEnableCallKitSwitch:(UISwitch *)sender {
DDLogInfo(@"%@ user toggled call kit preference: %@", self.tag, (sender.isOn ? @"ON" : @"OFF"));
[[Environment getCurrent].preferences setIsCallKitEnabled:sender.isOn];
[[Environment getCurrent].callService createCallUIAdapter];
}
#pragma mark - Util
- (BOOL)supportsCallKit
{
return SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0);
}
#pragma mark - Logging #pragma mark - Logging
+ (NSString *)tag + (NSString *)tag

@ -717,6 +717,9 @@
/* Navbar title */ /* Navbar title */
"SETTINGS_ABOUT" = "About"; "SETTINGS_ABOUT" = "About";
/* Short table cell label */
"SETTINGS_ADVANCED_CALLKIT_TITLE" = "Use CallKit";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"SETTINGS_ADVANCED_DEBUGLOG" = "Enable Debug Log"; "SETTINGS_ADVANCED_DEBUGLOG" = "Enable Debug Log";
@ -727,7 +730,7 @@
"SETTINGS_ADVANCED_TITLE" = "Advanced"; "SETTINGS_ADVANCED_TITLE" = "Advanced";
/* This setting is used to switch between new-style WebRTC calling and old-style RedPhone calling. */ /* This setting is used to switch between new-style WebRTC calling and old-style RedPhone calling. */
"SETTINGS_ADVANCED_WEBRTC" = "Enable WebRTC Calling"; "SETTINGS_ADVANCED_WEBRTC" = "Enable Video Calling (Beta)";
/* The message of the alert shown when updates to the WebRTC property fail. */ /* The message of the alert shown when updates to the WebRTC property fail. */
"SETTINGS_ADVANCED_WEBRTC_FAILED_MESSAGE" = "Could not update your preferences."; "SETTINGS_ADVANCED_WEBRTC_FAILED_MESSAGE" = "Could not update your preferences.";
@ -789,6 +792,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"SETTINGS_SCREEN_SECURITY_DETAIL" = "Prevents Signal previews from appearing in the app switcher."; "SETTINGS_SCREEN_SECURITY_DETAIL" = "Prevents Signal previews from appearing in the app switcher.";
/* Settings table section footer. */
"SETTINGS_SECTION_CALL_KIT_DESCRIPTION" = "CallKit allows you to answer calls directly from your lockscreen. Be aware that when using CallKit, Apple syncs some call metadata to your iCloud account.";
/* settings topic header for table section */
"SETTINGS_SECTION_TITLE_CALLING" = "Calling";
/* Section header */ /* Section header */
"SETTINGS_SECURITY_TITLE" = "Screen Security"; "SETTINGS_SECURITY_TITLE" = "Screen Security";

Loading…
Cancel
Save