// // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // import Foundation // This entity has responsibility for blocking the device from sleeping if // certain behaviors (e.g. recording or playing voice messages) are in progress. // // Sleep blocking is keyed using "block objects" whose lifetime corresponds to // the duration of the block. For example, sleep blocking during audio playback // can be keyed to the audio player. This provides a measure of robustness. // On the one hand, we can use weak references to track block objects and stop // blocking if the block object is deallocated even if removeBlock() is not // called. On the other hand, we will also get correct behavior to addBlock() // being called twice with the same block object. @objc public class DeviceSleepManager: NSObject { @objc public static let sharedInstance = DeviceSleepManager() private class SleepBlock: CustomDebugStringConvertible { weak var blockObject: NSObject? var debugDescription: String { return "SleepBlock(\(String(reflecting: blockObject)))" } init(blockObject: NSObject) { self.blockObject = blockObject } } private var blocks: [SleepBlock] = [] private override init() { super.init() SwiftSingletons.register(self) NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: NSNotification.Name.OWSApplicationDidEnterBackground, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } @objc private func didEnterBackground() { AssertIsOnMainThread() ensureSleepBlocking() } @objc public func addBlock(blockObject: NSObject) { AssertIsOnMainThread() blocks.append(SleepBlock(blockObject: blockObject)) ensureSleepBlocking() } @objc public func removeBlock(blockObject: NSObject) { AssertIsOnMainThread() blocks = blocks.filter { $0.blockObject != nil && $0.blockObject != blockObject } ensureSleepBlocking() } private func ensureSleepBlocking() { AssertIsOnMainThread() // Cull expired blocks. blocks = blocks.filter { $0.blockObject != nil } let shouldBlock = blocks.count > 0 CurrentAppContext().ensureSleepBlocking(shouldBlock, blockingObjects: blocks) } }