diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 6e8d225f5..780a47d72 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ 34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */; }; 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */; }; 34D2CCD4206294B900CB1A14 /* OWSScreenLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */; }; + 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */; }; 34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; }; 34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0241ED3673300188D7C /* DebugUIMessages.m */; }; 34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0261ED3673300188D7C /* DebugUITableViewController.m */; }; @@ -811,6 +812,8 @@ 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentUploadView.m; sourceTree = ""; }; 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRecipientStatusUtils.swift; sourceTree = ""; }; 34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSScreenLock.swift; sourceTree = ""; }; + 34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScreenLockUI.h; sourceTree = ""; }; + 34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScreenLockUI.m; sourceTree = ""; }; 34D5CCA71EAE3D30005515DB /* AvatarViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvatarViewHelper.h; sourceTree = ""; }; 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvatarViewHelper.m; sourceTree = ""; }; 34D8C0231ED3673300188D7C /* DebugUIMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMessages.h; sourceTree = ""; }; @@ -1972,6 +1975,8 @@ 340FC8CB20518C76007AEB0F /* OWSBackupJob.h */, 340FC8CC20518C76007AEB0F /* OWSBackupJob.m */, 34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */, + 34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */, + 34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */, 4579431C1E7C8CE9008ED0C0 /* Pastelog.h */, 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */, 450DF2041E0D74AC003D14BE /* Platform.swift */, @@ -3212,6 +3217,7 @@ 45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */, 340FC8A7204DAC8D007AEB0F /* RegistrationViewController.m in Sources */, 452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */, + 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */, 45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */, 345BC30C2047030700257B7C /* OWS2FASettingsViewController.m in Sources */, 340FC8CA20517B84007AEB0F /* OWSBackupImportJob.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 1f2fa00fd..6d2b0d1cd 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -12,6 +12,7 @@ #import "OWS2FASettingsViewController.h" #import "OWSBackup.h" #import "OWSNavigationController.h" +#import "OWSScreenLockUI.h" #import "Pastelog.h" #import "PushManager.h" #import "RegistrationViewController.h" @@ -66,15 +67,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; @property (nonatomic) BOOL areVersionMigrationsComplete; @property (nonatomic) BOOL didAppLaunchFail; -// Unlike UIApplication.applicationState, this state is -// updated conservatively, e.g. the flag is cleared during -// "will enter background." -@property (nonatomic) BOOL appIsInactive; -@property (nonatomic, nullable) NSDate *appBecameInactiveDate; -@property (nonatomic) UIWindow *screenBlockingWindow; -@property (nonatomic) BOOL hasUnlockedScreenLock; -@property (nonatomic) BOOL isShowingScreenLockUI; - @end #pragma mark - @@ -87,8 +79,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; DDLogWarn(@"%@ applicationDidEnterBackground.", self.logTag); [DDLog flushLog]; - - self.appIsInactive = YES; } - (void)applicationWillEnterForeground:(UIApplication *)application { @@ -200,7 +190,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self application:application didReceiveRemoteNotification:remoteNotif]; } - [self prepareScreenProtection]; + [OWSScreenLockUI.sharedManager setupWithRootWindow:self.window]; // Ensure OWSContactsSyncing is instantiated. [OWSContactsSyncing sharedManager]; @@ -217,10 +207,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; selector:@selector(registrationLockDidChange:) name:NSNotificationName_2FAStateDidChange object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(screenLockDidChange:) - name:OWSScreenLock.ScreenLockDidChange - object:nil]; DDLogInfo(@"%@ application: didFinishLaunchingWithOptions completed.", self.logTag); @@ -604,8 +590,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self handleActivation]; }]; - self.appIsInactive = NO; - DDLogInfo(@"%@ applicationDidBecomeActive completed.", self.logTag); } @@ -723,8 +707,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; DDLogWarn(@"%@ applicationWillResignActive.", self.logTag); - self.appIsInactive = YES; - __block OWSBackgroundTask *backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; [AppReadiness runNowOrWhenAppIsReady:^{ if ([TSAccountManager isRegistered]) { @@ -1152,8 +1134,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self ensureRootViewController]; [OWSBackup.sharedManager setup]; - - [self ensureScreenProtection]; } - (void)registrationStateDidChange @@ -1179,8 +1159,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; // For non-legacy users, read receipts are on by default. [OWSReadReceiptManager.sharedManager setAreReadReceiptsEnabled:YES]; } - - [self ensureScreenProtection]; } - (void)registrationLockDidChange:(NSNotification *)notification @@ -1215,151 +1193,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; } [AppUpdateNag.sharedInstance showAppUpgradeNagIfNecessary]; - - [self ensureScreenProtection]; -} - -#pragma mark - Screen Lock and Protection - -- (void)setAppIsInactive:(BOOL)appIsInactive -{ - if (appIsInactive) { - if (!_appIsInactive) { - // Whenever app becomes inactive, clear this state. - self.hasUnlockedScreenLock = NO; - - // Note the time when app became inactive. - self.appBecameInactiveDate = [NSDate new]; - } - } - - _appIsInactive = appIsInactive; - - [self ensureScreenProtection]; -} - -- (void)ensureScreenProtection -{ - OWSAssertIsOnMainThread(); - - if (!AppReadiness.isAppReady) { - [AppReadiness runNowOrWhenAppIsReady:^{ - [self ensureScreenProtection]; - }]; - return; - } - - // Don't show 'Screen Protection' if: - // - // * App is active or... - // * 'Screen Protection' is not enabled. - BOOL shouldHaveScreenProtection = (self.appIsInactive && Environment.preferences.screenSecurityIsEnabled); - - BOOL shouldHaveScreenLock = NO; - if (self.appIsInactive) { - // Don't show 'Screen Lock' if app is inactive. - } else if (![TSAccountManager isRegistered]) { - // Don't show 'Screen Lock' if user is not registered. - } else if (!OWSScreenLock.sharedManager.isScreenLockEnabled) { - // Don't show 'Screen Lock' if 'Screen Lock' isn't enabled. - } else if (self.hasUnlockedScreenLock) { - // Don't show 'Screen Lock' if 'Screen Lock' has been unlocked. - } else if (!self.appBecameInactiveDate) { - // Show 'Screen Lock' if app hasn't become inactive yet (just launched). - shouldHaveScreenLock = YES; - } else { - OWSAssert(self.appBecameInactiveDate); - - NSTimeInterval screenLockInterval = fabs([self.appBecameInactiveDate timeIntervalSinceNow]); - NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout; - OWSAssert(screenLockInterval >= 0); - OWSAssert(screenLockTimeout >= 0); - if (self.appBecameInactiveDate && screenLockInterval < screenLockTimeout) { - // Don't show 'Screen Lock' if 'Screen Lock' timeout hasn't elapsed. - shouldHaveScreenProtection = YES; - - // Check again when screen lock timeout should elapse. - NSTimeInterval screenLockRemaining = screenLockTimeout - screenLockInterval + 0.2f; - OWSAssert(screenLockRemaining >= 0); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(screenLockRemaining * NSEC_PER_SEC)), - dispatch_get_main_queue(), - ^{ - [self ensureScreenProtection]; - }); - } else { - // Otherwise, show 'Screen Lock'. - shouldHaveScreenLock = YES; - } - } - - BOOL shouldShowBlockWindow = shouldHaveScreenProtection || shouldHaveScreenLock; - self.screenBlockingWindow.hidden = !shouldShowBlockWindow; - - if (shouldHaveScreenLock) { - if (!self.isShowingScreenLockUI) { - self.isShowingScreenLockUI = YES; - - [OWSScreenLock.sharedManager tryToUnlockScreenLockWithSuccess:^{ - DDLogInfo(@"%@ unlock screen lock succeeded.", self.logTag); - self.isShowingScreenLockUI = NO; - self.hasUnlockedScreenLock = YES; - [self ensureScreenProtection]; - } - failure:^(NSError *error) { - DDLogInfo(@"%@ unlock screen lock failed.", self.logTag); - self.isShowingScreenLockUI = NO; - - [self showScreenLockFailureAlertWithMessage:error.localizedDescription]; - } - cancel:^{ - DDLogInfo(@"%@ unlock screen lock cancelled.", self.logTag); - self.isShowingScreenLockUI = NO; - - [self showScreenLockFailureAlertWithMessage: - NSLocalizedString(@"SCREEN_LOCK_UNLOCK_CANCELLED", - @"Message for alert indicating that screen lock unlock was cancelled.")]; - }]; - } - } -} - -- (void)showScreenLockFailureAlertWithMessage:(NSString *)message -{ - OWSAssertIsOnMainThread(); - - [OWSAlerts showAlertWithTitle:NSLocalizedString(@"SCREEN_LOCK_UNLOCK_FAILED", - @"Title for alert indicating that screen lock could not be unlocked.") - message:message - buttonTitle:nil - buttonAction:^(UIAlertAction *action) { - // After the alert, re-show the unlock UI. - [self ensureScreenProtection]; - }]; -} - -- (void)screenLockDidChange:(NSNotification *)notification -{ - [self ensureScreenProtection]; -} - -// 'Screen Blocking' window obscures the app screen: -// -// * In the app switcher. -// * During 'Screen Lock' unlock process. -- (void)prepareScreenProtection -{ - OWSAssertIsOnMainThread(); - - UIWindow *window = [[UIWindow alloc] initWithFrame:self.window.bounds]; - window.hidden = YES; - window.opaque = YES; - window.userInteractionEnabled = NO; - window.windowLevel = CGFLOAT_MAX; - window.backgroundColor = UIColor.ows_materialBlueColor; - window.rootViewController = - [[UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil] instantiateInitialViewController]; - - self.screenBlockingWindow = window; } @end diff --git a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m index e5c0a82d0..97b37ab0f 100644 --- a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m +++ b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m @@ -91,10 +91,6 @@ self.title = NSLocalizedString(@"SETTINGS_NAV_BAR_TITLE", @"Title for settings activity"); [self updateTableContents]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self showPrivacy]; - }); } - (void)viewWillAppear:(BOOL)animated diff --git a/Signal/src/ViewControllers/AppSettings/PrivacySettingsTableViewController.m b/Signal/src/ViewControllers/AppSettings/PrivacySettingsTableViewController.m index 0024a4db1..b0ec05742 100644 --- a/Signal/src/ViewControllers/AppSettings/PrivacySettingsTableViewController.m +++ b/Signal/src/ViewControllers/AppSettings/PrivacySettingsTableViewController.m @@ -167,26 +167,19 @@ NS_ASSUME_NONNULL_BEGIN }]]; [contents addSection:twoFactorAuthSection]; - // BOOL showScreenLockUI = OWSScreenLock.sharedManager.isScreenLockSupported; - //#ifdef DEBUG - // showScreenLockUI = YES; - //#endif - BOOL showScreenLockUI = YES; - if (showScreenLockUI) { - OWSTableSection *screenLockSection = [OWSTableSection new]; - screenLockSection.headerTitle = NSLocalizedString( - @"SETTINGS_SCREEN_LOCK_SECTION_TITLE", @"Title for the 'screen lock' section of the privacy settings."); - screenLockSection.footerTitle = NSLocalizedString( - @"SETTINGS_SCREEN_LOCK_SECTION_FOOTER", @"Footer for the 'screen lock' section of the privacy settings."); - [screenLockSection - addItem:[OWSTableItem - switchItemWithText:NSLocalizedString(@"SETTINGS_SCREEN_LOCK_SWITCH_LABEL", - @"Label for the 'enable screen lock' switch of the privacy settings.") - isOn:OWSScreenLock.sharedManager.isScreenLockEnabled - target:self - selector:@selector(isScreenLockEnabledDidChange:)]]; - [contents addSection:screenLockSection]; - } + OWSTableSection *screenLockSection = [OWSTableSection new]; + screenLockSection.headerTitle = NSLocalizedString( + @"SETTINGS_SCREEN_LOCK_SECTION_TITLE", @"Title for the 'screen lock' section of the privacy settings."); + screenLockSection.footerTitle = NSLocalizedString( + @"SETTINGS_SCREEN_LOCK_SECTION_FOOTER", @"Footer for the 'screen lock' section of the privacy settings."); + [screenLockSection + addItem:[OWSTableItem + switchItemWithText:NSLocalizedString(@"SETTINGS_SCREEN_LOCK_SWITCH_LABEL", + @"Label for the 'enable screen lock' switch of the privacy settings.") + isOn:OWSScreenLock.sharedManager.isScreenLockEnabled + target:self + selector:@selector(isScreenLockEnabledDidChange:)]]; + [contents addSection:screenLockSection]; self.contents = contents; } diff --git a/Signal/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 497a898c1..dccd63e15 100644 --- a/Signal/src/ViewControllers/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeViewController.m @@ -284,10 +284,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; } [self updateBarButtonItems]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self settingsButtonPressed:nil]; - }); } - (void)viewDidAppear:(BOOL)animated diff --git a/Signal/src/util/OWSScreenLock.swift b/Signal/src/util/OWSScreenLock.swift index 27f605eaf..752c47e6d 100644 --- a/Signal/src/util/OWSScreenLock.swift +++ b/Signal/src/util/OWSScreenLock.swift @@ -173,7 +173,7 @@ import LocalAuthentication let completion = { (outcome: OWSScreenLockOutcome) in switch outcome { case .failure(let error): - Logger.error("\(self.TAG) enable screen lock failed with error: \(error)") + Logger.error("\(self.TAG) local authentication failed with error: \(error)") default: break } @@ -189,13 +189,13 @@ import LocalAuthentication var authError: NSError? let canEvaluatePolicy = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) if !canEvaluatePolicy || authError != nil { - Logger.error("\(TAG) could not determine if screen lock is supported: \(String(describing: authError))") + Logger.error("\(TAG) could not determine if local authentication is supported: \(String(describing: authError))") let outcome = self.outcomeForLAError(errorParam: authError, defaultErrorDescription: defaultErrorDescription) switch outcome { case .success: - owsFail("\(self.TAG) unexpected success") + owsFail("\(self.TAG) local authentication unexpected success") completion(.failure(error:defaultErrorDescription)) case .cancel, .failure: completion(outcome) @@ -214,14 +214,14 @@ import LocalAuthentication context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: localizedReason) { success, evaluateError in if success { - Logger.info("\(self.TAG) enable screen lock succeeded.") + Logger.info("\(self.TAG) local authentication succeeded.") completion(.success) } else { let outcome = self.outcomeForLAError(errorParam: evaluateError, defaultErrorDescription: defaultErrorDescription) switch outcome { case .success: - owsFail("\(self.TAG) unexpected success") + owsFail("\(self.TAG) local authentication unexpected success") completion(.failure(error:defaultErrorDescription)) case .cancel, .failure: completion(outcome) diff --git a/Signal/src/util/OWSScreenLockUI.h b/Signal/src/util/OWSScreenLockUI.h new file mode 100644 index 000000000..90317d713 --- /dev/null +++ b/Signal/src/util/OWSScreenLockUI.h @@ -0,0 +1,17 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSScreenLockUI : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)sharedManager; + +- (void)setupWithRootWindow:(UIWindow *)rootWindow; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/OWSScreenLockUI.m b/Signal/src/util/OWSScreenLockUI.m new file mode 100644 index 000000000..66dcd121e --- /dev/null +++ b/Signal/src/util/OWSScreenLockUI.m @@ -0,0 +1,256 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "OWSScreenLockUI.h" +#import "Signal-Swift.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSScreenLockUI () + +// Unlike UIApplication.applicationState, this state is +// updated conservatively, e.g. the flag is cleared during +// "will enter background." +@property (nonatomic) BOOL appIsInactive; +@property (nonatomic, nullable) NSDate *appBecameInactiveDate; +@property (nonatomic) UIWindow *screenBlockingWindow; +@property (nonatomic) BOOL hasUnlockedScreenLock; +@property (nonatomic) BOOL isShowingScreenLockUI; + +@end + +#pragma mark - + +@implementation OWSScreenLockUI + ++ (instancetype)sharedManager +{ + static OWSScreenLockUI *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[self alloc] initDefault]; + }); + return instance; +} + +- (instancetype)initDefault +{ + self = [super init]; + + if (!self) { + return self; + } + + [self observeNotifications]; + + OWSSingletonAssert(); + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)observeNotifications +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:OWSApplicationDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillResignActive:) + name:OWSApplicationWillResignActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(registrationStateDidChange) + name:RegistrationStateDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(screenLockDidChange:) + name:OWSScreenLock.ScreenLockDidChange + object:nil]; +} + +- (void)setupWithRootWindow:(UIWindow *)rootWindow +{ + OWSAssertIsOnMainThread(); + OWSAssert(rootWindow); + + [self prepareScreenProtectionWithRootWindow:rootWindow]; + + [AppReadiness runNowOrWhenAppIsReady:^{ + [self ensureScreenProtection]; + }]; +} + +#pragma mark - Methods + +- (void)setAppIsInactive:(BOOL)appIsInactive +{ + if (appIsInactive) { + if (!_appIsInactive) { + // Whenever app becomes inactive, clear this state. + self.hasUnlockedScreenLock = NO; + + // Note the time when app became inactive. + self.appBecameInactiveDate = [NSDate new]; + } + } + + _appIsInactive = appIsInactive; + + [self ensureScreenProtection]; +} + +- (void)ensureScreenProtection +{ + OWSAssertIsOnMainThread(); + + if (!AppReadiness.isAppReady) { + [AppReadiness runNowOrWhenAppIsReady:^{ + [self ensureScreenProtection]; + }]; + return; + } + + // Don't show 'Screen Protection' if: + // + // * App is active or... + // * 'Screen Protection' is not enabled. + BOOL shouldHaveScreenProtection = (self.appIsInactive && Environment.preferences.screenSecurityIsEnabled); + + BOOL shouldHaveScreenLock = NO; + if (self.appIsInactive) { + // Don't show 'Screen Lock' if app is inactive. + } else if (![TSAccountManager isRegistered]) { + // Don't show 'Screen Lock' if user is not registered. + } else if (!OWSScreenLock.sharedManager.isScreenLockEnabled) { + // Don't show 'Screen Lock' if 'Screen Lock' isn't enabled. + } else if (self.hasUnlockedScreenLock) { + // Don't show 'Screen Lock' if 'Screen Lock' has been unlocked. + } else if (!self.appBecameInactiveDate) { + // Show 'Screen Lock' if app hasn't become inactive yet (just launched). + shouldHaveScreenLock = YES; + } else { + OWSAssert(self.appBecameInactiveDate); + + NSTimeInterval screenLockInterval = fabs([self.appBecameInactiveDate timeIntervalSinceNow]); + NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout; + OWSAssert(screenLockInterval >= 0); + OWSAssert(screenLockTimeout >= 0); + if (self.appBecameInactiveDate && screenLockInterval < screenLockTimeout) { + // Don't show 'Screen Lock' if 'Screen Lock' timeout hasn't elapsed. + shouldHaveScreenProtection = YES; + + // Check again when screen lock timeout should elapse. + NSTimeInterval screenLockRemaining = screenLockTimeout - screenLockInterval + 0.2f; + OWSAssert(screenLockRemaining >= 0); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(screenLockRemaining * NSEC_PER_SEC)), + dispatch_get_main_queue(), + ^{ + [self ensureScreenProtection]; + }); + } else { + // Otherwise, show 'Screen Lock'. + shouldHaveScreenLock = YES; + } + } + + BOOL shouldShowBlockWindow = shouldHaveScreenProtection || shouldHaveScreenLock; + self.screenBlockingWindow.hidden = !shouldShowBlockWindow; + + if (shouldHaveScreenLock) { + if (!self.isShowingScreenLockUI) { + self.isShowingScreenLockUI = YES; + + [OWSScreenLock.sharedManager tryToUnlockScreenLockWithSuccess:^{ + DDLogInfo(@"%@ unlock screen lock succeeded.", self.logTag); + self.isShowingScreenLockUI = NO; + self.hasUnlockedScreenLock = YES; + [self ensureScreenProtection]; + } + failure:^(NSError *error) { + DDLogInfo(@"%@ unlock screen lock failed.", self.logTag); + self.isShowingScreenLockUI = NO; + + [self showScreenLockFailureAlertWithMessage:error.localizedDescription]; + } + cancel:^{ + DDLogInfo(@"%@ unlock screen lock cancelled.", self.logTag); + self.isShowingScreenLockUI = NO; + + [self showScreenLockFailureAlertWithMessage: + NSLocalizedString(@"SCREEN_LOCK_UNLOCK_CANCELLED", + @"Message for alert indicating that screen lock unlock was cancelled.")]; + }]; + } + } +} + +- (void)showScreenLockFailureAlertWithMessage:(NSString *)message +{ + OWSAssertIsOnMainThread(); + + [OWSAlerts showAlertWithTitle:NSLocalizedString(@"SCREEN_LOCK_UNLOCK_FAILED", + @"Title for alert indicating that screen lock could not be unlocked.") + message:message + buttonTitle:nil + buttonAction:^(UIAlertAction *action) { + // After the alert, re-show the unlock UI. + [self ensureScreenProtection]; + }]; +} + +// 'Screen Blocking' window obscures the app screen: +// +// * In the app switcher. +// * During 'Screen Lock' unlock process. +- (void)prepareScreenProtectionWithRootWindow:(UIWindow *)rootWindow +{ + OWSAssertIsOnMainThread(); + OWSAssert(rootWindow); + + UIWindow *window = [[UIWindow alloc] initWithFrame:rootWindow.bounds]; + window.hidden = YES; + window.opaque = YES; + window.userInteractionEnabled = NO; + window.windowLevel = CGFLOAT_MAX; + window.backgroundColor = UIColor.ows_materialBlueColor; + window.rootViewController = + [[UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil] instantiateInitialViewController]; + + self.screenBlockingWindow = window; +} + +#pragma mark - Events + +- (void)screenLockDidChange:(NSNotification *)notification +{ + [self ensureScreenProtection]; +} + +- (void)registrationStateDidChange +{ + OWSAssertIsOnMainThread(); + + DDLogInfo(@"registrationStateDidChange"); + + [self ensureScreenProtection]; +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + self.appIsInactive = NO; +} + +- (void)applicationWillResignActive:(NSNotification *)notification +{ + self.appIsInactive = YES; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.h b/SignalServiceKit/src/Messages/OWSBlockingManager.h index 56757f75e..25dd3d24f 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.h +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.h @@ -29,4 +29,5 @@ extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange; @end + NS_ASSUME_NONNULL_END