users can opt out of CallKit

// FREEBIE
This commit is contained in:
Michael Kirk 2017-02-02 12:21:48 -05:00
parent d8df4b9e35
commit 2a9aa4c852
9 changed files with 178 additions and 53 deletions

View file

@ -204,6 +204,7 @@ import Foundation
}
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,
mode:AVAudioSessionModeVoiceChat,
options:AVAudioSessionCategoryOptions(rawValue: 0))

View file

@ -100,9 +100,13 @@ protocol CallServiceObserver: class {
// MARK: Dependencies
let accountManager: AccountManager
let messageSender: MessageSender
var callUIAdapter: CallUIAdapter!
private let accountManager: AccountManager
private let messageSender: MessageSender
private let contactsManager: OWSContactsManager
private let notificationsAdapter: CallNotificationsAdapter
// Exposed by environment.m
internal var callUIAdapter: CallUIAdapter!
// MARK: Class
@ -179,11 +183,13 @@ protocol CallServiceObserver: class {
required init(accountManager: AccountManager, contactsManager: OWSContactsManager, messageSender: MessageSender, notificationsAdapter: CallNotificationsAdapter) {
self.accountManager = accountManager
self.contactsManager = contactsManager
self.messageSender = messageSender
self.notificationsAdapter = notificationsAdapter
super.init()
self.callUIAdapter = CallUIAdapter(callService: self, contactsManager: contactsManager, notificationsAdapter: notificationsAdapter)
self.createCallUIAdapter()
NotificationCenter.default.addObserver(self,
selector:#selector(didEnterBackground),
@ -213,6 +219,16 @@ protocol CallServiceObserver: class {
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: Notifications

View file

@ -10,14 +10,12 @@ import Foundation
@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) {
init(redphoneManager: PhoneManager, contactsManager: OWSContactsManager, contactsUpdater: ContactsUpdater) {
self.redphoneManager = redphoneManager
self.callUIAdapter = callUIAdapter
self.contactsManager = contactsManager
self.contactsUpdater = contactsUpdater
@ -93,6 +91,14 @@ import Foundation
}
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)
return true
}

View file

@ -77,11 +77,11 @@ extension CallUIAdaptee {
// So we use the non-CallKit call UI.
Logger.info("\(TAG) choosing non-callkit adaptee for simulator.")
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+")
adaptee = CallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
} else {
Logger.info("\(TAG) choosing non-callkit adaptee for older iOS")
Logger.info("\(TAG) choosing non-callkit adaptee")
adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
}

View file

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

View file

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

View file

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

View file

@ -17,18 +17,26 @@ NS_ASSUME_NONNULL_BEGIN
@interface AdvancedSettingsTableViewController ()
@property NSArray *sectionsArray;
@property (nonatomic) UITableViewCell *enableWebRTCCell;
@property (nonatomic) UITableViewCell *enableCallKitCell;
@property (nonatomic) UITableViewCell *enableLogCell;
@property (nonatomic) UITableViewCell *submitLogCell;
@property (nonatomic) UITableViewCell *registerPushCell;
@property (nonatomic) UISwitch *enableWebRTCSwitch;
@property (nonatomic) UISwitch *enableCallKitSwitch;
@property (nonatomic) UISwitch *enableLogSwitch;
@property (nonatomic, readonly) BOOL supportsCallKit;
@end
typedef NS_ENUM(NSInteger, AdvancedSettingsTableViewControllerSection) {
AdvancedSettingsTableViewControllerSectionLogging,
AdvancedSettingsTableViewControllerSectionCalling,
AdvancedSettingsTableViewControllerSectionPushNotifications,
AdvancedSettingsTableViewControllerSection_Count // meta section
};
@implementation AdvancedSettingsTableViewController
- (void)viewDidLoad {
@ -37,16 +45,13 @@ NS_ASSUME_NONNULL_BEGIN
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
}
- (instancetype)init {
self.sectionsArray = @[
NSLocalizedString(@"LOGGING_SECTION", nil),
NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts")
];
- (instancetype)init
{
return [super initWithStyle:UITableViewStyleGrouped];
}
- (void)loadView {
- (void)loadView
{
[super loadView];
self.title = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE", @"");
@ -62,6 +67,16 @@ NS_ASSUME_NONNULL_BEGIN
action:@selector(didToggleEnableWebRTCSwitch:)
forControlEvents:UIControlEventTouchUpInside];
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
self.enableLogCell = [[UITableViewCell alloc] init];
@ -85,14 +100,18 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return (NSInteger)[self.sectionsArray count];
return AdvancedSettingsTableViewControllerSection_Count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case 0:
return 1 + (self.enableLogSwitch.isOn ? 2 : 1);
case 1:
AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)section;
switch (settingsSection) {
case AdvancedSettingsTableViewControllerSectionLogging:
return self.enableLogSwitch.isOn ? 2 : 1;
case AdvancedSettingsTableViewControllerSectionCalling:
return self.supportsCallKit ? 2 : 1;
case AdvancedSettingsTableViewControllerSectionPushNotifications:
return 1;
default:
return 0;
@ -101,26 +120,61 @@ NS_ASSUME_NONNULL_BEGIN
- (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 {
if (indexPath.section == 0) {
switch (indexPath.row) {
case 0:
return self.enableWebRTCCell;
case 1:
return self.enableLogCell;
case 2:
return self.enableLogSwitch.isOn ? self.submitLogCell : self.registerPushCell;
}
} else {
return self.registerPushCell;
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
AdvancedSettingsTableViewControllerSection settingsSection = (AdvancedSettingsTableViewControllerSection)section;
switch (settingsSection) {
case AdvancedSettingsTableViewControllerSectionCalling:
return NSLocalizedString(@"SETTINGS_SECTION_CALL_KIT_DESCRIPTION", @"Settings table section footer.");
default:
return nil;
}
}
NSAssert(false, @"No Cell configured");
return nil;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
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 {
@ -207,6 +261,19 @@ NS_ASSUME_NONNULL_BEGIN
[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
+ (NSString *)tag

View file

@ -717,6 +717,9 @@
/* Navbar title */
"SETTINGS_ABOUT" = "About";
/* Short table cell label */
"SETTINGS_ADVANCED_CALLKIT_TITLE" = "Use CallKit";
/* No comment provided by engineer. */
"SETTINGS_ADVANCED_DEBUGLOG" = "Enable Debug Log";
@ -727,7 +730,7 @@
"SETTINGS_ADVANCED_TITLE" = "Advanced";
/* 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. */
"SETTINGS_ADVANCED_WEBRTC_FAILED_MESSAGE" = "Could not update your preferences.";
@ -789,6 +792,12 @@
/* No comment provided by engineer. */
"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 */
"SETTINGS_SECURITY_TITLE" = "Screen Security";