Merge branch 'mkirk/callKitPrivacyVsiOS11'

pull/1/head
Michael Kirk 7 years ago
commit 75e3516ebc

@ -1026,9 +1026,19 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
updateRemoteVideoLayout()
}
internal func dismissIfPossible(shouldDelay: Bool, ignoreNag: Bool = false, completion: (() -> Swift.Void)? = nil) {
internal func dismissIfPossible(shouldDelay: Bool, ignoreNag ignoreNagParam: Bool = false, completion: (() -> Swift.Void)? = nil) {
callUIAdapter.audioService.delegate = nil
let ignoreNag: Bool = {
// Nothing to nag about on iOS11
if #available(iOS 11, *) {
return true
} else {
// otherwise on iOS10, nag as specified
return ignoreNagParam
}
}()
if hasDismissed {
// Don't dismiss twice.
return

@ -1,8 +1,10 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "NotificationSettingsOptionsViewController.h"
#import "Signal-Swift.h"
#import "SignalApp.h"
#import <SignalMessaging/Environment.h>
@implementation NotificationSettingsOptionsViewController
@ -48,6 +50,10 @@
- (void)setNotificationType:(NotificationType)notificationType
{
[Environment.preferences setNotificationPreviewType:notificationType];
// rebuild callUIAdapter since notification configuration changed.
[SignalApp.sharedApp.callService createCallUIAdapter];
[self.navigationController popViewControllerAnimated:YES];
}

@ -56,7 +56,7 @@
[contents addSection:soundsSection];
OWSTableSection *backgroundSection = [OWSTableSection new];
backgroundSection.headerTitle = NSLocalizedString(@"NOTIFICATIONS_SECTION_BACKGROUND", nil);
backgroundSection.headerTitle = NSLocalizedString(@"SETTINGS_NOTIFICATION_CONTENT_TITLE", @"table section header");
[backgroundSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:@"UITableViewCellStyleValue1"];
@ -74,6 +74,8 @@
[NotificationSettingsOptionsViewController new];
[weakSelf.navigationController pushViewController:vc animated:YES];
}]];
backgroundSection.footerTitle
= NSLocalizedString(@"SETTINGS_NOTIFICATION_CONTENT_DESCRIPTION", @"table section footer");
[contents addSection:backgroundSection];
OWSTableSection *inAppSection = [OWSTableSection new];

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "PrivacySettingsTableViewController.h"
@ -80,7 +80,19 @@ NS_ASSUME_NONNULL_BEGIN
selector:@selector(didToggleCallsHideIPAddressSwitch:)]];
[contents addSection:callingSection];
if ([UIDevice currentDevice].supportsCallKit) {
if (@available(iOS 11, *)) {
OWSTableSection *callKitSection = [OWSTableSection new];
[callKitSection
addItem:[OWSTableItem switchItemWithText:NSLocalizedString(
@"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_TITLE",
@"Short table cell label")
isOn:[Environment.preferences isSystemCallLogEnabled]
target:weakSelf
selector:@selector(didToggleEnableSystemCallLogSwitch:)]];
callKitSection.footerTitle = NSLocalizedString(
@"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_DESCRIPTION", @"Settings table section footer.");
[contents addSection:callKitSection];
} else if (@available(iOS 10, *)) {
OWSTableSection *callKitSection = [OWSTableSection new];
callKitSection.footerTitle
= NSLocalizedString(@"SETTINGS_SECTION_CALL_KIT_DESCRIPTION", @"Settings table section footer.");
@ -173,17 +185,32 @@ NS_ASSUME_NONNULL_BEGIN
[Environment.preferences setDoCallsHideIPAddress:enabled];
}
- (void)didToggleEnableSystemCallLogSwitch:(UISwitch *)sender
{
DDLogInfo(@"%@ user toggled call kit preference: %@", self.logTag, (sender.isOn ? @"ON" : @"OFF"));
[[Environment current].preferences setIsSystemCallLogEnabled:sender.isOn];
// rebuild callUIAdapter since CallKit configuration changed.
[SignalApp.sharedApp.callService createCallUIAdapter];
}
- (void)didToggleEnableCallKitSwitch:(UISwitch *)sender {
DDLogInfo(@"%@ user toggled call kit preference: %@", self.logTag, (sender.isOn ? @"ON" : @"OFF"));
[[Environment current].preferences setIsCallKitEnabled:sender.isOn];
// rebuild callUIAdapter since CallKit vs not changed.
[SignalApp.sharedApp.callService createCallUIAdapter];
// Show/Hide dependent switch: CallKit privacy
[self updateTableContents];
}
- (void)didToggleEnableCallKitPrivacySwitch:(UISwitch *)sender {
DDLogInfo(@"%@ user toggled call kit privacy preference: %@", self.logTag, (sender.isOn ? @"ON" : @"OFF"));
[[Environment current].preferences setIsCallKitPrivacyEnabled:!sender.isOn];
// rebuild callUIAdapter since CallKit configuration changed.
[SignalApp.sharedApp.callService createCallUIAdapter];
}
#pragma mark - Log util

@ -123,8 +123,7 @@ protocol CallAudioServiceDelegate: class {
super.init()
// This fails when someone toggles iOS Call Integration
SwiftSingletons.register(self)
// We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings
// Configure audio session so we don't prompt user with Record permission until call is connected.

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import UIKit
@ -18,26 +18,29 @@ import SignalServiceKit
final class CallKitCallManager: NSObject {
let callController = CXCallController()
let showNamesOnCallScreen: Bool
static let kAnonymousCallHandlePrefix = "Signal:"
override required init() {
required init(showNamesOnCallScreen: Bool) {
AssertIsOnMainThread()
self.showNamesOnCallScreen = showNamesOnCallScreen
super.init()
SwiftSingletons.register(self)
// We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings
}
// MARK: Actions
func startCall(_ call: SignalCall) {
var handle: CXHandle
if Environment.current().preferences.isCallKitPrivacyEnabled() {
if showNamesOnCallScreen {
handle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
} else {
let callKitId = CallKitCallManager.kAnonymousCallHandlePrefix + call.localId.uuidString
handle = CXHandle(type: .generic, value: callKitId)
TSStorageManager.shared().setPhoneNumber(call.remotePhoneNumber, forCallKitId:callKitId)
} else {
handle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
}
let startCallAction = CXStartCallAction(call: call.localId, handle: handle)

@ -24,14 +24,15 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
internal let callService: CallService
internal let notificationsAdapter: CallNotificationsAdapter
internal let contactsManager: OWSContactsManager
private let showNamesOnCallScreen: Bool
private let provider: CXProvider
let audioActivity: AudioActivity
private let audioActivity: AudioActivity
// CallKit handles incoming ringer stop/start for us. Yay!
let hasManualRinger = false
// The app's provider configuration, representing its CallKit capabilities
static var providerConfiguration: CXProviderConfiguration {
class func buildProviderConfiguration(useSystemCallLog: Bool) -> CXProviderConfiguration {
let localizedName = NSLocalizedString("APPLICATION_NAME", comment: "Name of application")
let providerConfiguration = CXProviderConfiguration(localizedName: localizedName)
@ -50,24 +51,35 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
// default iOS ringtone OR the custom ringtone associated with this user's
// system contact, if possible (iOS 11 or later).
if #available(iOS 11.0, *) {
providerConfiguration.includesCallsInRecents = useSystemCallLog
} else {
// not configurable for iOS10+
assert(useSystemCallLog)
}
return providerConfiguration
}
init(callService: CallService, contactsManager: OWSContactsManager, notificationsAdapter: CallNotificationsAdapter) {
init(callService: CallService, contactsManager: OWSContactsManager, notificationsAdapter: CallNotificationsAdapter, showNamesOnCallScreen: Bool, useSystemCallLog: Bool) {
AssertIsOnMainThread()
Logger.debug("\(self.TAG) \(#function)")
self.callManager = CallKitCallManager()
self.callManager = CallKitCallManager(showNamesOnCallScreen: showNamesOnCallScreen)
self.callService = callService
self.contactsManager = contactsManager
self.notificationsAdapter = notificationsAdapter
self.provider = CXProvider(configuration: type(of: self).providerConfiguration)
let providerConfiguration = type(of: self).buildProviderConfiguration(useSystemCallLog: useSystemCallLog)
self.provider = CXProvider(configuration: providerConfiguration)
self.audioActivity = AudioActivity(audioDescription: "[CallKitCallUIAdaptee]")
self.showNamesOnCallScreen = showNamesOnCallScreen
super.init()
SwiftSingletons.register(self)
// We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings
self.provider.setDelegate(self, queue: nil)
}
@ -112,14 +124,15 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
// Construct a CXCallUpdate describing the incoming call, including the caller.
let update = CXCallUpdate()
if Environment.current().preferences.isCallKitPrivacyEnabled() {
if showNamesOnCallScreen {
update.localizedCallerName = self.contactsManager.stringForConversationTitle(withPhoneIdentifier: call.remotePhoneNumber)
update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
} else {
let callKitId = CallKitCallManager.kAnonymousCallHandlePrefix + call.localId.uuidString
update.remoteHandle = CXHandle(type: .generic, value: callKitId)
TSStorageManager.shared().setPhoneNumber(call.remotePhoneNumber, forCallKitId: callKitId)
update.localizedCallerName = NSLocalizedString("CALLKIT_ANONYMOUS_CONTACT_NAME", comment: "The generic name used for calls if CallKit privacy is enabled")
} else {
update.localizedCallerName = self.contactsManager.stringForConversationTitle(withPhoneIdentifier: call.remotePhoneNumber)
update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
}
update.hasVideo = call.hasLocalVideo
@ -254,8 +267,9 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
action.fulfill()
self.provider.reportOutgoingCall(with: call.localId, startedConnectingAt: nil)
if Environment.current().preferences.isCallKitPrivacyEnabled() {
// Update the name used in the CallKit UI for outgoing calls.
// Update the name used in the CallKit UI for outgoing calls when the user prefers not to show names
// in ther notifications
if !showNamesOnCallScreen {
let update = CXCallUpdate()
update.localizedCallerName = NSLocalizedString("CALLKIT_ANONYMOUS_CONTACT_NAME",
comment: "The generic name used for calls if CallKit privacy is enabled")

@ -91,9 +91,21 @@ 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 11, *) {
Logger.info("\(TAG) choosing callkit adaptee for iOS11+")
let showNames = Environment.preferences().notificationPreviewType() != .noNameNoPreview
let useSystemCallLog = Environment.preferences().isSystemCallLogEnabled()
adaptee = CallKitCallUIAdaptee(callService: callService, contactsManager: contactsManager, notificationsAdapter: notificationsAdapter, showNamesOnCallScreen: showNames, useSystemCallLog: useSystemCallLog)
} else if #available(iOS 10.0, *), Environment.current().preferences.isCallKitEnabled() {
Logger.info("\(TAG) choosing callkit adaptee for iOS10+")
adaptee = CallKitCallUIAdaptee(callService: callService, contactsManager: contactsManager, notificationsAdapter: notificationsAdapter)
Logger.info("\(TAG) choosing callkit adaptee for iOS10")
let hideNames = Environment.preferences().isCallKitPrivacyEnabled() || Environment.preferences().notificationPreviewType() == .noNameNoPreview
let showNames = !hideNames
// All CallKit calls use the system call log on iOS10
let useSystemCallLog = true
adaptee = CallKitCallUIAdaptee(callService: callService, contactsManager: contactsManager, notificationsAdapter: notificationsAdapter, showNamesOnCallScreen: showNames, useSystemCallLog: useSystemCallLog)
} else {
Logger.info("\(TAG) choosing non-callkit adaptee")
adaptee = NonCallKitCallUIAdaptee(callService: callService, notificationsAdapter: notificationsAdapter)
@ -103,7 +115,7 @@ extension CallUIAdaptee {
super.init()
SwiftSingletons.register(self)
// We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings
callService.addObserverAndSyncState(observer: self)
}

@ -112,10 +112,8 @@
}
case NotificationNameNoPreview:
case NotificationNamePreview: {
alertMessage = (([UIDevice currentDevice].supportsCallKit &&
[[Environment current].preferences isCallKitPrivacyEnabled])
? [CallStrings missedCallNotificationBodyWithoutCallerName]
: [NSString stringWithFormat:[CallStrings missedCallNotificationBodyWithCallerName], callerName]);
alertMessage =
[NSString stringWithFormat:[CallStrings missedCallNotificationBodyWithCallerName], callerName];
break;
}
}
@ -152,12 +150,8 @@
}
case NotificationNameNoPreview:
case NotificationNamePreview: {
alertMessage = (([UIDevice currentDevice].supportsCallKit &&
[[Environment current].preferences isCallKitPrivacyEnabled])
? [CallStrings missedCallWithIdentityChangeNotificationBodyWithoutCallerName]
: [NSString
stringWithFormat:[CallStrings missedCallWithIdentityChangeNotificationBodyWithCallerName],
callerName]);
alertMessage = [NSString
stringWithFormat:[CallStrings missedCallWithIdentityChangeNotificationBodyWithCallerName], callerName];
break;
}
}
@ -193,12 +187,8 @@
}
case NotificationNameNoPreview:
case NotificationNamePreview: {
alertMessage = (([UIDevice currentDevice].supportsCallKit &&
[[Environment current].preferences isCallKitPrivacyEnabled])
? [CallStrings missedCallWithIdentityChangeNotificationBodyWithoutCallerName]
: [NSString
stringWithFormat:[CallStrings missedCallWithIdentityChangeNotificationBodyWithCallerName],
callerName]);
alertMessage = [NSString
stringWithFormat:[CallStrings missedCallWithIdentityChangeNotificationBodyWithCallerName], callerName];
break;
}
}

@ -1147,10 +1147,7 @@
"NOTIFICATIONS_FOOTER_WARNING" = "Due to known bugs in Apple's push framework, message previews will only be shown if the message is retrieved within 30 seconds after being sent. The application badge might be inaccurate as a result.";
/* No comment provided by engineer. */
"NOTIFICATIONS_NONE" = "No name or message";
/* No comment provided by engineer. */
"NOTIFICATIONS_SECTION_BACKGROUND" = "Background Notifications";
"NOTIFICATIONS_NONE" = "No Name or Content";
/* No comment provided by engineer. */
"NOTIFICATIONS_SECTION_INAPP" = "In-App Notifications";
@ -1159,10 +1156,10 @@
"NOTIFICATIONS_SECTION_SOUNDS" = "Sounds";
/* No comment provided by engineer. */
"NOTIFICATIONS_SENDER_AND_MESSAGE" = "Sender name & message";
"NOTIFICATIONS_SENDER_AND_MESSAGE" = "Name and Content";
/* No comment provided by engineer. */
"NOTIFICATIONS_SENDER_ONLY" = "Sender name only";
"NOTIFICATIONS_SENDER_ONLY" = "Name Only";
/* No comment provided by engineer. */
"NOTIFICATIONS_SHOW" = "Show";
@ -1575,12 +1572,24 @@
/* Title for settings activity */
"SETTINGS_NAV_BAR_TITLE" = "Settings";
/* table section footer */
"SETTINGS_NOTIFICATION_CONTENT_DESCRIPTION" = "Call and Message notifications can appear while your phone is locked. You may wish to limit what is shown in these notifications.";
/* table section header */
"SETTINGS_NOTIFICATION_CONTENT_TITLE" = "Notification Content";
/* No comment provided by engineer. */
"SETTINGS_NOTIFICATIONS" = "Notifications";
/* Label for 'CallKit privacy' preference */
"SETTINGS_PRIVACY_CALLKIT_PRIVACY_TITLE" = "Show Caller's Name & Number";
/* Settings table section footer. */
"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_DESCRIPTION" = "Disabling this will prevent calls from appearing in the \"Recents\" list in the iOS Phone app.";
/* Short table cell label */
"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_TITLE" = "System Call Logs";
/* Short table cell label */
"SETTINGS_PRIVACY_CALLKIT_TITLE" = "iOS Call Integration";

@ -59,8 +59,14 @@ extern NSString *const OWSPreferencesKeyEnableDebugLog;
#pragma mark Callkit
- (BOOL)isSystemCallLogEnabled;
- (void)setIsSystemCallLogEnabled:(BOOL)flag;
#pragma mark - Legacy CallKit settings
- (BOOL)isCallKitEnabled;
- (void)setIsCallKitEnabled:(BOOL)flag;
// Returns YES IFF isCallKitEnabled has been set by user.
- (BOOL)isCallKitEnabledSet;

@ -26,6 +26,7 @@ NSString *const OWSPreferencesKeyCallsHideIPAddress = @"CallsHideIPAddress";
NSString *const OWSPreferencesKeyHasDeclinedNoContactsView = @"hasDeclinedNoContactsView";
NSString *const OWSPreferencesKeyIOSUpgradeNagDate = @"iOSUpgradeNagDate";
NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExtensions_5";
NSString *const OWSPreferencesKeySystemCallLogEnabled = @"OWSPreferencesKeySystemCallLogEnabled";
@implementation OWSPreferences
@ -173,36 +174,122 @@ NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExten
#pragma mark CallKit
- (BOOL)isSystemCallLogEnabled
{
if (@available(iOS 11, *)) {
// do nothing
} else {
OWSFail(@"%@ Call Logging can only be configured on iOS11+", self.logTag);
return NO;
}
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeySystemCallLogEnabled];
if (preference) {
return preference.boolValue;
} else {
// For legacy users, who may have previously intentionally disabled CallKit because they
// didn't want their calls showing up in the call log, we want to disable call logging
NSNumber *callKitPreference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled];
if (callKitPreference && !callKitPreference.boolValue) {
// user explicitly opted out of callKit, so disable system call logging.
return NO;
}
}
// For everyone else, including new users, enable by default.
return YES;
}
- (void)setIsSystemCallLogEnabled:(BOOL)flag
{
if (@available(iOS 11, *)) {
// do nothing
} else {
OWSFail(@"%@ Call Logging can only be configured on iOS11+", self.logTag);
return;
}
[self setValueForKey:OWSPreferencesKeySystemCallLogEnabled toValue:@(flag)];
}
// In iOS 10.2.1, Apple fixed a bug wherein call history was backed up to iCloud.
//
// See: https://support.apple.com/en-us/HT207482
//
// In iOS 11, Apple introduced a property CXProviderConfiguration.includesCallsInRecents
// that allows us to prevent Signal calls made with CallKit from showing up in the device's
// call history.
//
// Therefore in versions of iOS after 11, we have no need of call privacy.
#pragma mark Legacy CallKit
- (BOOL)isCallKitEnabled
{
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return NO;
}
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled];
return preference ? [preference boolValue] : YES;
}
- (void)setIsCallKitEnabled:(BOOL)flag
{
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return;
}
[self setValueForKey:OWSPreferencesKeyCallKitEnabled toValue:@(flag)];
OWSFail(@"Rev callUIAdaptee to get new setting");
}
- (BOOL)isCallKitEnabledSet
{
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return NO;
}
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled];
return preference != nil;
}
- (BOOL)isCallKitPrivacyEnabled
{
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled];
return preference ? [preference boolValue] : YES;
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return NO;
}
NSNumber *_Nullable preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled];
if (preference) {
return [preference boolValue];
} else {
// Private by default.
return YES;
}
}
- (void)setIsCallKitPrivacyEnabled:(BOOL)flag
{
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return;
}
[self setValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled toValue:@(flag)];
}
- (BOOL)isCallKitPrivacySet
{
if (@available(iOS 11, *)) {
OWSFail(@"%@ CallKit privacy is irrelevant for iOS11+", self.logTag);
return NO;
}
NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled];
return preference != nil;
}

Loading…
Cancel
Save