diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index a88139623..82e37075d 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -94,6 +94,7 @@ static NSTimeInterval launchStartedAt; // The user experience is reasonable: if the user activates the app // while in landscape, the user sees a rotation animation. @property (nonatomic) BOOL isLandscapeEnabled; +@property (nonatomic) BOOL shouldEnableLandscape; @property (nonatomic, nullable) NSTimer *landscapeTimer; @end @@ -180,7 +181,7 @@ static NSTimeInterval launchStartedAt; - (void)applicationDidEnterBackground:(UIApplication *)application { OWSLogWarn(@"applicationDidEnterBackground."); - [self disableLandscape]; + [self updateShouldEnableLandscape]; [DDLog flushLog]; } @@ -188,7 +189,7 @@ static NSTimeInterval launchStartedAt; - (void)applicationWillEnterForeground:(UIApplication *)application { OWSLogWarn(@"applicationWillEnterForeground."); - [self disableLandscape]; + [self updateShouldEnableLandscape]; } - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application @@ -309,6 +310,14 @@ static NSTimeInterval launchStartedAt; selector:@selector(registrationLockDidChange:) name:NSNotificationName_2FAStateDidChange object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(isScreenBlockActiveDidChange:) + name:IsScreenBlockActiveDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(reportedApplicationStateDidChange:) + name:ReportedApplicationStateDidChangeNotification + object:nil]; OWSLogInfo(@"application: didFinishLaunchingWithOptions completed."); @@ -651,7 +660,7 @@ static NSTimeInterval launchStartedAt; // be called _before_ we become active. [self clearAllNotificationsAndRestoreBadgeCount]; - [self enableLandscapeAfterDelay]; + [self updateShouldEnableLandscape]; OWSLogInfo(@"applicationDidBecomeActive completed."); } @@ -766,7 +775,7 @@ static NSTimeInterval launchStartedAt; OWSLogWarn(@"applicationWillResignActive."); - [self disableLandscape]; + [self updateShouldEnableLandscape]; [DDLog flushLog]; } @@ -988,6 +997,11 @@ static NSTimeInterval launchStartedAt; if (!self.isLandscapeEnabled) { return UIInterfaceOrientationMaskPortrait; } + // We use isAppForegroundAndActive which depends on "reportedApplicationState" + // and therefore is more conservative about being active. + if (!CurrentAppContext().isAppForegroundAndActive) { + return UIInterfaceOrientationMaskPortrait; + } // This clause shouldn't be necessary, but it's nice to // be explicit about our invariants. if (!self.hasInitialRootViewController) { @@ -1026,17 +1040,49 @@ static NSTimeInterval launchStartedAt; } // See comments on isLandscapeEnabled property. -- (void)disableLandscape +- (void)updateShouldEnableLandscape { OWSAssertIsOnMainThread(); - BOOL wasEnabled = self.isLandscapeEnabled; - self.isLandscapeEnabled = NO; - [self.landscapeTimer invalidate]; - self.landscapeTimer = nil; + // We use isAppForegroundAndActive which depends on "reportedApplicationState" + // and therefore is more conservative about being active. + self.shouldEnableLandscape = (CurrentAppContext().isAppForegroundAndActive && [AppReadiness isAppReady] + && ![OWSWindowManager sharedManager].isScreenBlockActive); +} + +// See comments on isLandscapeEnabled property. +- (void)setShouldEnableLandscape:(BOOL)shouldEnableLandscape +{ + if (_shouldEnableLandscape == shouldEnableLandscape) { + return; + } + + _shouldEnableLandscape = shouldEnableLandscape; + + void (^disableLandscape)(void) = ^{ + BOOL wasEnabled = self.isLandscapeEnabled; + self.isLandscapeEnabled = NO; + [self.landscapeTimer invalidate]; + self.landscapeTimer = nil; + + if (wasEnabled) { + [UIViewController attemptRotationToDeviceOrientation]; + } + }; + + if (shouldEnableLandscape) { + disableLandscape(); - if (wasEnabled) { - [UIViewController attemptRotationToDeviceOrientation]; + // Enable Async + NSTimeInterval delay = 0.35f; + self.landscapeTimer = [NSTimer weakScheduledTimerWithTimeInterval:delay + target:self + selector:@selector(enableLandscape) + userInfo:nil + repeats:NO]; + } else { + // Disable. + disableLandscape(); } } @@ -1052,20 +1098,6 @@ static NSTimeInterval launchStartedAt; [UIViewController attemptRotationToDeviceOrientation]; } -// See comments on isLandscapeEnabled property. -- (void)enableLandscapeAfterDelay -{ - OWSAssertIsOnMainThread(); - - [self disableLandscape]; - NSTimeInterval delay = 0.35f; - self.landscapeTimer = [NSTimer weakScheduledTimerWithTimeInterval:delay - target:self - selector:@selector(enableLandscape) - userInfo:nil - repeats:NO]; -} - #pragma mark Push Notifications Delegate Methods - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { @@ -1326,6 +1358,8 @@ static NSTimeInterval launchStartedAt; [self.primaryStorage touchDbAsync]; + [self updateShouldEnableLandscape]; + // Every time the user upgrades to a new version: // // * Update account attributes. @@ -1388,6 +1422,16 @@ static NSTimeInterval launchStartedAt; [self enableBackgroundRefreshIfNecessary]; } +- (void)isScreenBlockActiveDidChange:(NSNotification *)notification +{ + [self updateShouldEnableLandscape]; +} + +- (void)reportedApplicationStateDidChange:(NSNotification *)notification +{ + [self updateShouldEnableLandscape]; +} + - (void)ensureRootViewController { OWSAssertIsOnMainThread(); diff --git a/Signal/src/util/MainAppContext.h b/Signal/src/util/MainAppContext.h index 5800a48f4..45ba1a8e5 100644 --- a/Signal/src/util/MainAppContext.h +++ b/Signal/src/util/MainAppContext.h @@ -1,11 +1,13 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN +extern NSString *const ReportedApplicationStateDidChangeNotification; + @interface MainAppContext : NSObject @end diff --git a/Signal/src/util/MainAppContext.m b/Signal/src/util/MainAppContext.m index cda535adf..8631e9291 100644 --- a/Signal/src/util/MainAppContext.m +++ b/Signal/src/util/MainAppContext.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "MainAppContext.h" @@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN +NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplicationStateDidChangeNotification"; + @interface MainAppContext () @property (atomic) UIApplicationState reportedApplicationState; @@ -74,6 +76,20 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Notifications +- (void)setReportedApplicationState:(UIApplicationState)reportedApplicationState +{ + OWSAssertIsOnMainThread(); + + if (_reportedApplicationState == reportedApplicationState) { + return; + } + _reportedApplicationState = reportedApplicationState; + + [[NSNotificationCenter defaultCenter] postNotificationName:ReportedApplicationStateDidChangeNotification + object:nil + userInfo:nil]; +} + - (void)applicationWillEnterForeground:(NSNotification *)notification { OWSAssertIsOnMainThread(); diff --git a/SignalMessaging/utils/OWSWindowManager.h b/SignalMessaging/utils/OWSWindowManager.h index cfebd30d7..78195032b 100644 --- a/SignalMessaging/utils/OWSWindowManager.h +++ b/SignalMessaging/utils/OWSWindowManager.h @@ -4,6 +4,10 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString *const OWSWindowManagerCallDidChangeNotification; + +extern NSString *const IsScreenBlockActiveDidChangeNotification; + // This VC can become first responder // when presented to ensure that the input accessory is updated. @interface OWSWindowRootViewController : UIViewController @@ -12,7 +16,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -extern NSString *const OWSWindowManagerCallDidChangeNotification; const CGFloat OWSWindowManagerCallBannerHeight(void); extern const UIWindowLevel UIWindowLevel_Background; @@ -28,8 +31,7 @@ extern const UIWindowLevel UIWindowLevel_Background; @property (nonatomic, readonly) UIWindow *rootWindow; @property (nonatomic, readonly) UIWindow *menuActionsWindow; - -- (void)setIsScreenBlockActive:(BOOL)isScreenBlockActive; +@property (nonatomic) BOOL isScreenBlockActive; - (BOOL)isAppWindow:(UIWindow *)window; diff --git a/SignalMessaging/utils/OWSWindowManager.m b/SignalMessaging/utils/OWSWindowManager.m index 5731de17d..057a0d624 100644 --- a/SignalMessaging/utils/OWSWindowManager.m +++ b/SignalMessaging/utils/OWSWindowManager.m @@ -13,6 +13,8 @@ NS_ASSUME_NONNULL_BEGIN NSString *const OWSWindowManagerCallDidChangeNotification = @"OWSWindowManagerCallDidChangeNotification"; +NSString *const IsScreenBlockActiveDidChangeNotification = @"IsScreenBlockActiveDidChangeNotification"; + const CGFloat OWSWindowManagerCallBannerHeight(void) { if (@available(iOS 11.4, *)) { @@ -146,8 +148,6 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) // UIWindowLevel_ScreenBlocking() if active. @property (nonatomic) UIWindow *screenBlockingWindow; -@property (nonatomic) BOOL isScreenBlockActive; - @property (nonatomic) BOOL shouldShowCallView; @property (nonatomic, nullable) UIViewController *callViewController; @@ -308,6 +308,10 @@ const UIWindowLevel UIWindowLevel_MessageActions(void) _isScreenBlockActive = isScreenBlockActive; [self ensureWindowState]; + + [[NSNotificationCenter defaultCenter] postNotificationName:IsScreenBlockActiveDidChangeNotification + object:nil + userInfo:nil]; } - (BOOL)isAppWindow:(UIWindow *)window