From 5b9ab0cf5d6202695b7f9e6b0cc8fd98dd278d32 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 23 Feb 2018 10:52:46 -0500 Subject: [PATCH 1/3] Auto-disable CallKit privacy in iOS 11 and later. --- .../PrivacySettingsTableViewController.m | 4 +-- .../Speakerbox/CallKitCallUIAdaptee.swift | 4 +++ SignalMessaging/utils/OWSPreferences.h | 1 + SignalMessaging/utils/OWSPreferences.m | 30 +++++++++++++++++-- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Signal/src/ViewControllers/PrivacySettingsTableViewController.m b/Signal/src/ViewControllers/PrivacySettingsTableViewController.m index 8c32e799c..6dcf6ff7c 100644 --- a/Signal/src/ViewControllers/PrivacySettingsTableViewController.m +++ b/Signal/src/ViewControllers/PrivacySettingsTableViewController.m @@ -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" @@ -89,7 +89,7 @@ NS_ASSUME_NONNULL_BEGIN isOn:[Environment.preferences isCallKitEnabled] target:weakSelf selector:@selector(didToggleEnableCallKitSwitch:)]]; - if (Environment.preferences.isCallKitEnabled) { + if (Environment.preferences.isCallKitEnabled && !Environment.preferences.isCallKitPrivacyAutoDisabled) { [callKitSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_PRIVACY_CALLKIT_PRIVACY_TITLE", @"Label for 'CallKit privacy' preference") diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index e0888d6db..b1531400a 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -50,6 +50,10 @@ 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 = false + } + return providerConfiguration } diff --git a/SignalMessaging/utils/OWSPreferences.h b/SignalMessaging/utils/OWSPreferences.h index 4000ae1a9..6daaf8656 100644 --- a/SignalMessaging/utils/OWSPreferences.h +++ b/SignalMessaging/utils/OWSPreferences.h @@ -68,6 +68,7 @@ extern NSString *const OWSPreferencesKeyEnableDebugLog; - (void)setIsCallKitPrivacyEnabled:(BOOL)flag; // Returns YES IFF isCallKitPrivacyEnabled has been set by user. - (BOOL)isCallKitPrivacySet; +- (BOOL)isCallKitPrivacyAutoDisabled; #pragma mark direct call connectivity (non-TURN) diff --git a/SignalMessaging/utils/OWSPreferences.m b/SignalMessaging/utils/OWSPreferences.m index ea084b5fb..4de2c2d0d 100644 --- a/SignalMessaging/utils/OWSPreferences.m +++ b/SignalMessaging/utils/OWSPreferences.m @@ -190,10 +190,36 @@ NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExten return preference != nil; } +- (BOOL)isCallKitPrivacyAutoDisabled +{ + // 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. + if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(11, 0)) { + return YES; + } else { + return NO; + } +} + - (BOOL)isCallKitPrivacyEnabled { - NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled]; - return preference ? [preference boolValue] : YES; + if ([self isCallKitPrivacyAutoDisabled]) { + return NO; + } + NSNumber *_Nullable preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled]; + if (preference) { + return [preference boolValue]; + } else { + // Private by default. + return YES; + } } - (void)setIsCallKitPrivacyEnabled:(BOOL)flag From 5959cdf070282b4de10ad2666e605f88e4a74cce Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 23 Feb 2018 17:08:21 -0500 Subject: [PATCH 2/3] Simplify call privacy settings We've long allowed users to configure what shows up in message notifications: - name: content (by default) - just name (no content) - generic notification (no name nor content) Now we're dual purposing that setting to apply to calls. If someone doesn't want to show names in the message notifications, presumably also don't want that name showing up in the call log. Also, since the earlier CallKit/iCloud issues had been addressed before iOS11, we upgrade all iOS11 users to the more intuitive CallKit interface. Also: introduce "use system call logs" toggle when available. It will be enabled by default, but we disable it for legacy users who'd explicitly opted *out* of CallKit. // FREEBIE --- .../ViewControllers/CallViewController.swift | 12 ++- ...otificationSettingsOptionsViewController.m | 8 +- .../NotificationSettingsViewController.m | 4 +- .../PrivacySettingsTableViewController.m | 31 +++++- Signal/src/call/CallAudioService.swift | 4 +- .../call/Speakerbox/CallKitCallManager.swift | 16 +-- .../Speakerbox/CallKitCallUIAdaptee.swift | 37 ++++--- .../call/UserInterface/CallUIAdapter.swift | 19 +++- Signal/src/environment/NotificationsManager.m | 22 ++--- .../translations/en.lproj/Localizable.strings | 21 ++-- SignalMessaging/utils/OWSPreferences.h | 7 +- SignalMessaging/utils/OWSPreferences.m | 99 +++++++++++++++---- 12 files changed, 209 insertions(+), 71 deletions(-) diff --git a/Signal/src/ViewControllers/CallViewController.swift b/Signal/src/ViewControllers/CallViewController.swift index 9b92ba729..b796eab29 100644 --- a/Signal/src/ViewControllers/CallViewController.swift +++ b/Signal/src/ViewControllers/CallViewController.swift @@ -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 diff --git a/Signal/src/ViewControllers/NotificationSettingsOptionsViewController.m b/Signal/src/ViewControllers/NotificationSettingsOptionsViewController.m index 628e6501e..04a83c3e2 100644 --- a/Signal/src/ViewControllers/NotificationSettingsOptionsViewController.m +++ b/Signal/src/ViewControllers/NotificationSettingsOptionsViewController.m @@ -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 @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]; } diff --git a/Signal/src/ViewControllers/NotificationSettingsViewController.m b/Signal/src/ViewControllers/NotificationSettingsViewController.m index df653f7e9..fdd986d69 100644 --- a/Signal/src/ViewControllers/NotificationSettingsViewController.m +++ b/Signal/src/ViewControllers/NotificationSettingsViewController.m @@ -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]; diff --git a/Signal/src/ViewControllers/PrivacySettingsTableViewController.m b/Signal/src/ViewControllers/PrivacySettingsTableViewController.m index 6dcf6ff7c..c57ed4b5d 100644 --- a/Signal/src/ViewControllers/PrivacySettingsTableViewController.m +++ b/Signal/src/ViewControllers/PrivacySettingsTableViewController.m @@ -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."); @@ -89,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN isOn:[Environment.preferences isCallKitEnabled] target:weakSelf selector:@selector(didToggleEnableCallKitSwitch:)]]; - if (Environment.preferences.isCallKitEnabled && !Environment.preferences.isCallKitPrivacyAutoDisabled) { + if (Environment.preferences.isCallKitEnabled) { [callKitSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_PRIVACY_CALLKIT_PRIVACY_TITLE", @"Label for 'CallKit privacy' preference") @@ -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 diff --git a/Signal/src/call/CallAudioService.swift b/Signal/src/call/CallAudioService.swift index 4560ca631..a841bab60 100644 --- a/Signal/src/call/CallAudioService.swift +++ b/Signal/src/call/CallAudioService.swift @@ -123,8 +123,8 @@ 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 + // SwiftSingletons.register(self) // Configure audio session so we don't prompt user with Record permission until call is connected. diff --git a/Signal/src/call/Speakerbox/CallKitCallManager.swift b/Signal/src/call/Speakerbox/CallKitCallManager.swift index fff993181..545e08c80 100644 --- a/Signal/src/call/Speakerbox/CallKitCallManager.swift +++ b/Signal/src/call/Speakerbox/CallKitCallManager.swift @@ -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,30 @@ 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 + // SwiftSingletons.register(self) } // 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) diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index b1531400a..d96fdd7f2 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -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) @@ -51,27 +52,35 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { // system contact, if possible (iOS 11 or later). if #available(iOS 11.0, *) { - providerConfiguration.includesCallsInRecents = false + 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 + // SwiftSingletons.register(self) self.provider.setDelegate(self, queue: nil) } @@ -116,14 +125,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 @@ -258,8 +268,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") diff --git a/Signal/src/call/UserInterface/CallUIAdapter.swift b/Signal/src/call/UserInterface/CallUIAdapter.swift index df518ff1f..3956b1a4f 100644 --- a/Signal/src/call/UserInterface/CallUIAdapter.swift +++ b/Signal/src/call/UserInterface/CallUIAdapter.swift @@ -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,8 @@ extension CallUIAdaptee { super.init() - SwiftSingletons.register(self) + // We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings + // SwiftSingletons.register(self) callService.addObserverAndSyncState(observer: self) } diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index 03d660b0b..5d2fc8271 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -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; } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0ae24fd8f..bfc1dc1bb 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -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"; diff --git a/SignalMessaging/utils/OWSPreferences.h b/SignalMessaging/utils/OWSPreferences.h index 6daaf8656..f68aeb28e 100644 --- a/SignalMessaging/utils/OWSPreferences.h +++ b/SignalMessaging/utils/OWSPreferences.h @@ -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; @@ -68,7 +74,6 @@ extern NSString *const OWSPreferencesKeyEnableDebugLog; - (void)setIsCallKitPrivacyEnabled:(BOOL)flag; // Returns YES IFF isCallKitPrivacyEnabled has been set by user. - (BOOL)isCallKitPrivacySet; -- (BOOL)isCallKitPrivacyAutoDisabled; #pragma mark direct call connectivity (non-TURN) diff --git a/SignalMessaging/utils/OWSPreferences.m b/SignalMessaging/utils/OWSPreferences.m index 4de2c2d0d..d61369b23 100644 --- a/SignalMessaging/utils/OWSPreferences.m +++ b/SignalMessaging/utils/OWSPreferences.m @@ -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,46 +174,96 @@ 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 expclitily 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 { - NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled]; - return preference != nil; -} - -- (BOOL)isCallKitPrivacyAutoDisabled -{ - // 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. - if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(11, 0)) { - return YES; - } else { + 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 { - if ([self isCallKitPrivacyAutoDisabled]) { + 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]; @@ -224,11 +275,21 @@ NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExten - (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; } From 658b8c3223215e4942cc6f20e7a2cb958f0d6593 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 26 Feb 2018 15:16:26 -0500 Subject: [PATCH 3/3] CR: typos and doc changes // FREEBIE --- Signal/src/call/CallAudioService.swift | 1 - Signal/src/call/Speakerbox/CallKitCallManager.swift | 1 - Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift | 1 - Signal/src/call/UserInterface/CallUIAdapter.swift | 1 - SignalMessaging/utils/OWSPreferences.m | 2 +- 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Signal/src/call/CallAudioService.swift b/Signal/src/call/CallAudioService.swift index a841bab60..cc61ca389 100644 --- a/Signal/src/call/CallAudioService.swift +++ b/Signal/src/call/CallAudioService.swift @@ -124,7 +124,6 @@ protocol CallAudioServiceDelegate: class { super.init() // We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings - // SwiftSingletons.register(self) // Configure audio session so we don't prompt user with Record permission until call is connected. diff --git a/Signal/src/call/Speakerbox/CallKitCallManager.swift b/Signal/src/call/Speakerbox/CallKitCallManager.swift index 545e08c80..7b3e7ae97 100644 --- a/Signal/src/call/Speakerbox/CallKitCallManager.swift +++ b/Signal/src/call/Speakerbox/CallKitCallManager.swift @@ -28,7 +28,6 @@ final class CallKitCallManager: NSObject { super.init() // We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings - // SwiftSingletons.register(self) } // MARK: Actions diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index d96fdd7f2..bd9d9f87c 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -80,7 +80,6 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { super.init() // We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings - // SwiftSingletons.register(self) self.provider.setDelegate(self, queue: nil) } diff --git a/Signal/src/call/UserInterface/CallUIAdapter.swift b/Signal/src/call/UserInterface/CallUIAdapter.swift index 3956b1a4f..fa82b7729 100644 --- a/Signal/src/call/UserInterface/CallUIAdapter.swift +++ b/Signal/src/call/UserInterface/CallUIAdapter.swift @@ -116,7 +116,6 @@ extension CallUIAdaptee { super.init() // We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings - // SwiftSingletons.register(self) callService.addObserverAndSyncState(observer: self) } diff --git a/SignalMessaging/utils/OWSPreferences.m b/SignalMessaging/utils/OWSPreferences.m index d61369b23..afdda8394 100644 --- a/SignalMessaging/utils/OWSPreferences.m +++ b/SignalMessaging/utils/OWSPreferences.m @@ -192,7 +192,7 @@ NSString *const OWSPreferencesKeySystemCallLogEnabled = @"OWSPreferencesKeySyste // 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 expclitily opted out of callKit, so disable system call logging. + // user explicitly opted out of callKit, so disable system call logging. return NO; } }