diff --git a/Signal/AppLaunch/AppDelegate.swift b/Signal/AppLaunch/AppDelegate.swift index 34dfa2c12b..b10c4562c3 100644 --- a/Signal/AppLaunch/AppDelegate.swift +++ b/Signal/AppLaunch/AppDelegate.swift @@ -1768,7 +1768,15 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { return false } let isVideo = isVideoCall(intent) - appReadiness.runNowOrWhenAppDidBecomeReadySync { + + Task { @MainActor [appReadiness, screenLockUI] in + do { + try await appReadiness.waitForAppReady() + try await screenLockUI.waitForScreenUnlockThrowingPrevious() + } catch { + return + } + let tsAccountManager = DependenciesBridge.shared.tsAccountManager guard tsAccountManager.registrationStateWithMaybeSneakyTransaction.isRegistered else { Logger.warn("Ignoring user activity; not registered.") @@ -1799,6 +1807,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } callService.initiateCall(to: callTarget, isVideo: isVideo) } + return true } @@ -1928,7 +1937,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { withCompletionHandler completionHandler: @escaping () -> Void, ) { let startDate = MonotonicDate() - Task { @MainActor [appReadiness] () -> Void in + Task { @MainActor [appReadiness, screenLockUI] () -> Void in defer { completionHandler() } try await self.appReadiness.waitForAppReady() @@ -1942,7 +1951,11 @@ extension AppDelegate: UNUserNotificationCenterDelegate { let elapsedDuration = (MonotonicDate() - startDate).seconds try await withCooperativeTimeout(seconds: 27 - elapsedDuration) { // Do the actual thing we care about. - try await NotificationActionHandler.handleNotificationResponse(response, appReadiness: appReadiness) + try await NotificationActionHandler.handleNotificationResponse( + response, + appReadiness: appReadiness, + screenLockUI: screenLockUI, + ) // Then wait for any enqueued messages (e.g., read receipts) to be sent. try await backgroundMessageFetcher.waitForFetchingProcessingAndSideEffects() diff --git a/Signal/Notifications/NotificationActionHandler.swift b/Signal/Notifications/NotificationActionHandler.swift index 00683200ce..4beeb2e1d8 100644 --- a/Signal/Notifications/NotificationActionHandler.swift +++ b/Signal/Notifications/NotificationActionHandler.swift @@ -15,6 +15,7 @@ public class NotificationActionHandler { class func handleNotificationResponse( _ response: UNNotificationResponse, appReadiness: AppReadinessSetter, + screenLockUI: ScreenLockUI, ) async throws { owsAssertDebug(appReadiness.isAppReady) @@ -63,6 +64,7 @@ public class NotificationActionHandler { } switch responseAction { case .callBack: + try await screenLockUI.waitForScreenUnlockThrowingPrevious() try await self.callBack(userInfo: userInfo) case .markAsRead: try await markAsRead(userInfo: userInfo) diff --git a/Signal/util/ScreenLockUI.swift b/Signal/util/ScreenLockUI.swift index 04ab1e8ab2..de1f4d1c5f 100644 --- a/Signal/util/ScreenLockUI.swift +++ b/Signal/util/ScreenLockUI.swift @@ -65,7 +65,39 @@ class ScreenLockUI { // * 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. - private var isScreenLockLocked: Bool = false + private var isScreenLockLocked: Bool = false { + didSet { + AssertIsOnMainThread() + guard !isScreenLockLocked else { return } + if let pending = pendingScreenUnlockContinuation { + pendingScreenUnlockContinuation = nil + pending.resume() + } + } + } + + struct ScreenUnlockActionReplacedError: Error {} + + private var pendingScreenUnlockContinuation: CheckedContinuation? + + @MainActor + func waitForScreenUnlockThrowingPrevious() async throws { + AssertIsOnMainThread() + + tryToActivateScreenLockBasedOnCountdown() + ensureUI() + + if !isScreenLockLocked { + return + } + if let existing = pendingScreenUnlockContinuation { + pendingScreenUnlockContinuation = nil + existing.resume(throwing: ScreenUnlockActionReplacedError()) + } + try await withCheckedThrowingContinuation { continuation in + self.pendingScreenUnlockContinuation = continuation + } + } // The "countdown" until screen lock takes effect. private var screenLockCountdownTimestamp: UInt64?