Permalink
Browse files

Merge remote-tracking branch 'origin/charlesmchen/screenLockRework_' …

…into release/2.23.2
  • Loading branch information...
michaelkirk-signal committed Apr 6, 2018
2 parents 0a20eec + 2b210c7 commit 018a35df7b42b4941cb4dfc9f462b37c3fafd9e9
Showing with 107 additions and 86 deletions.
  1. +6 −0 Signal/src/util/OWSScreenLock.swift
  2. +101 −86 Signal/src/util/OWSScreenLockUI.m
@@ -24,6 +24,7 @@ import LocalAuthentication
0
]

@objc public static let ScreenLockWasEnabled = Notification.Name("ScreenLockWasEnabled")
@objc public static let ScreenLockDidChange = Notification.Name("ScreenLockDidChange")

let primaryStorage: OWSPrimaryStorage
@@ -86,8 +87,13 @@ import LocalAuthentication
AssertIsOnMainThread()
assert(OWSStorage.isStorageReady())

let isEnabling = value && !isScreenLockEnabled()

self.dbConnection.setBool(value, forKey: OWSScreenLock_Key_IsScreenLockEnabled, inCollection: OWSScreenLock_Collection)

if isEnabling {
NotificationCenter.default.postNotificationNameAsync(OWSScreenLock.ScreenLockWasEnabled, object: nil)
}
NotificationCenter.default.postNotificationNameAsync(OWSScreenLock.ScreenLockDidChange, object: nil)
}

@@ -31,12 +31,23 @@ @interface OWSScreenLockUI ()
// UI is dismissing.
@property (nonatomic) BOOL shouldClearAuthUIWhenActive;

@property (nonatomic, nullable) NSDate *appEnteredBackgroundDate;
@property (nonatomic, nullable) NSDate *appEnteredForegroundDate;
@property (nonatomic, nullable) NSDate *lastUnlockSuccessDate;
// Indicates whether or not the user is currently locked out of
// the app. Only applies if OWSScreenLock.isScreenLockEnabled.
//
// * The user is locked out out by default on app launch.
// * The user is also locked out if they spend more than
// "timeout" seconds outside the app. When the user leaves
// the app, a "countdown" begins.
@property (nonatomic) BOOL isScreenLockUnlocked;

@property (nonatomic, nullable) NSDate *screenLockCountdownDate;

// We normally start the "countdown" when the app enters the background,
// But we also want to start the "countdown" if the app is inactive for
// more than N seconds.
@property (nonatomic, nullable) NSTimer *inactiveTimer;


@end

#pragma mark -
@@ -99,6 +110,10 @@ - (void)observeNotifications
selector:@selector(screenLockDidChange:)
name:OWSScreenLock.ScreenLockDidChange
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(screenLockWasEnabled:)
name:OWSScreenLock.ScreenLockWasEnabled
object:nil];
}

- (void)setupWithRootWindow:(UIWindow *)rootWindow
@@ -115,70 +130,74 @@ - (void)setupWithRootWindow:(UIWindow *)rootWindow

#pragma mark - Methods

- (void)setAppIsInactive:(BOOL)appIsInactive
- (void)tryToActivateScreenLockUponBecomingActive
{
BOOL didChange = _appIsInactive != appIsInactive;
OWSAssert(!self.appIsInactive);

if (!self.isScreenLockUnlocked) {
// Screen lock is already activated.
DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 0", self.logTag);
return;
}
if (!self.screenLockCountdownDate) {
// We became inactive, but never started a countdown.
DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 1", self.logTag);
return;
}
NSTimeInterval countdownInterval = fabs([self.screenLockCountdownDate timeIntervalSinceNow]);
OWSAssert(countdownInterval >= 0);
NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout;
OWSAssert(screenLockTimeout >= 0);
if (countdownInterval >= screenLockTimeout) {
self.isScreenLockUnlocked = NO;
DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive YES 1 (%0.3f >= %0.3f)",
self.logTag,
countdownInterval,
screenLockTimeout);
} else {
DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 2 (%0.3f < %0.3f)",
self.logTag,
countdownInterval,
screenLockTimeout);
}
}

- (void)setAppIsInactive:(BOOL)appIsInactive
{
_appIsInactive = appIsInactive;

if (didChange) {
// If app is inactive for more than N seconds,
// treat this as "entering the background" for the purposes
// of Screen Lock.
if (!appIsInactive) {
[self.inactiveTimer invalidate];
self.inactiveTimer = nil;
} else if (!self.isShowingScreenLockUI) {
[self.inactiveTimer invalidate];
self.inactiveTimer = [NSTimer weakScheduledTimerWithTimeInterval:45.f
target:self
selector:@selector(inactiveTimerDidFire)
userInfo:nil
repeats:NO];
}
if (!appIsInactive) {
[self tryToActivateScreenLockUponBecomingActive];

self.screenLockCountdownDate = nil;
}

[self startInactiveTimerIfNecessary];

[self ensureScreenProtection];
}

- (void)setAppIsInBackground:(BOOL)appIsInBackground
{
if (appIsInBackground) {
if (!_appIsInBackground) {
[self markAppAsInBackground];
}
if (appIsInBackground && !_appIsInBackground) {
[self startScreenLockCountdownIfNecessary];
}

_appIsInBackground = appIsInBackground;

[self ensureScreenProtection];
}

- (void)markAppAsInBackground
- (void)startScreenLockCountdownIfNecessary
{
// Record the time when app entered background.
BOOL shouldResetEnteredBackgroundDate = NO;
if (!self.appEnteredBackgroundDate) {
// If this is the first time we're entering the
// background, record the date.
shouldResetEnteredBackgroundDate = YES;
}
if (self.hasUnlockedScreenLock) {
// If we've unlocked the screen lock, record the date.
shouldResetEnteredBackgroundDate = YES;
} else {
// If we're returning to the background _without_
// having unlocked the screen lock, DO NOT update this
// value as that would reset the unlock timeout.
}
if (shouldResetEnteredBackgroundDate) {
self.appEnteredBackgroundDate = [NSDate new];
if (!self.screenLockCountdownDate) {
DDLogVerbose(@"%@ startScreenLockCountdownIfNecessary.", self.logTag);
self.screenLockCountdownDate = [NSDate new];
}

self.didLastUnlockAttemptFail = NO;

[self.inactiveTimer invalidate];
self.inactiveTimer = nil;
[self clearInactiveTimer];
}

- (void)ensureScreenProtection
@@ -232,8 +251,11 @@ - (void)tryToPresentScreenLockUI

[OWSScreenLock.sharedManager tryToUnlockScreenLockWithSuccess:^{
DDLogInfo(@"%@ unlock screen lock succeeded.", self.logTag);

self.isShowingScreenLockUI = NO;
self.lastUnlockSuccessDate = [NSDate new];

self.isScreenLockUnlocked = YES;

[self ensureScreenProtection];
}
failure:^(NSError *error) {
@@ -284,17 +306,6 @@ - (BOOL)shouldHaveScreenProtection
}
}

- (BOOL)hasUnlockedScreenLock
{
if (!self.lastUnlockSuccessDate) {
return NO;
} else if (!self.appEnteredBackgroundDate) {
return YES;
} else {
return [self.lastUnlockSuccessDate isAfterDate:self.appEnteredBackgroundDate];
}
}

- (BOOL)shouldHaveScreenLock
{
if (![TSAccountManager isRegistered]) {
@@ -305,10 +316,6 @@ - (BOOL)shouldHaveScreenLock
// Don't show 'Screen Lock' if 'Screen Lock' isn't enabled.
DDLogVerbose(@"%@ shouldHaveScreenLock NO 2.", self.logTag);
return NO;
} else if (self.hasUnlockedScreenLock) {
// Don't show 'Screen Lock' if 'Screen Lock' has been unlocked.
DDLogVerbose(@"%@ shouldHaveScreenLock NO 3.", self.logTag);
return NO;
} else if (self.appIsInBackground) {
// Don't show 'Screen Lock' if app is in background.
DDLogVerbose(@"%@ shouldHaveScreenLock NO 4.", self.logTag);
@@ -322,26 +329,10 @@ - (BOOL)shouldHaveScreenLock
// Don't show 'Screen Lock' if app is inactive.
DDLogVerbose(@"%@ shouldHaveScreenLock NO 5.", self.logTag);
return NO;
} else if (!self.appEnteredBackgroundDate) {
// Show 'Screen Lock' if app has just launched.
DDLogVerbose(@"%@ shouldHaveScreenLock YES 1.", self.logTag);
return YES;
} else {
OWSAssert(self.appEnteredBackgroundDate);

NSTimeInterval screenLockInterval = fabs([self.appEnteredBackgroundDate timeIntervalSinceNow]);
NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout;
OWSAssert(screenLockInterval >= 0);
OWSAssert(screenLockTimeout >= 0);
if (screenLockInterval < screenLockTimeout) {
// Don't show 'Screen Lock' if 'Screen Lock' timeout hasn't elapsed.
DDLogVerbose(@"%@ shouldHaveScreenLock NO 6.", self.logTag);
return NO;
} else {
// Otherwise, show 'Screen Lock'.
DDLogVerbose(@"%@ shouldHaveScreenLock YES 2.", self.logTag);
return YES;
}
BOOL shouldHaveScreenLock = !self.isScreenLockUnlocked;
DDLogVerbose(@"%@ shouldHaveScreenLock ? %d.", self.logTag, shouldHaveScreenLock);
return shouldHaveScreenLock;
}
}

@@ -504,6 +495,16 @@ - (void)screenLockDidChange:(NSNotification *)notification
[self ensureScreenProtection];
}

- (void)screenLockWasEnabled:(NSNotification *)notification
{
// When we enable screen lock, consider that an unlock.
self.isScreenLockUnlocked = YES;

DDLogVerbose(@"%@ screenLockWasEnabled", self.logTag);

[self ensureScreenProtection];
}

- (void)registrationStateDidChange
{
OWSAssertIsOnMainThread();
@@ -542,23 +543,37 @@ - (void)applicationWillResignActive:(NSNotification *)notification

- (void)applicationWillEnterForeground:(NSNotification *)notification
{
// Clear the "delay Screen Lock UI" state; we don't want any
// delays when presenting the "unlock screen lock UI" after
// returning from background.
self.lastUnlockSuccessDate = nil;

self.appIsInBackground = NO;
self.appEnteredForegroundDate = [NSDate new];
}

- (void)applicationDidEnterBackground:(NSNotification *)notification
{
self.appIsInBackground = YES;
}

#pragma mark - Inactive Timer

- (void)inactiveTimerDidFire
{
[self markAppAsInBackground];
[self startScreenLockCountdownIfNecessary];
}

- (void)startInactiveTimerIfNecessary
{
if (self.appIsInactive && !self.isShowingScreenLockUI && !self.inactiveTimer) {
[self.inactiveTimer invalidate];
self.inactiveTimer = [NSTimer weakScheduledTimerWithTimeInterval:45.f
target:self
selector:@selector(inactiveTimerDidFire)
userInfo:nil
repeats:NO];
}
}

- (void)clearInactiveTimer
{
[self.inactiveTimer invalidate];
self.inactiveTimer = nil;
}

@end

0 comments on commit 018a35d

Please sign in to comment.