Wakeup for watch tasks

This commit is contained in:
Peter 2019-02-19 00:06:46 +03:00
parent c481383e05
commit b2962b0dce
9 changed files with 126 additions and 53 deletions

View File

@ -13,7 +13,7 @@ private final class SharedExtensionContext {
init(sharedContext: SharedAccountContext) {
self.sharedContext = sharedContext
self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
return sharedContext.accountUserInterfaceInUse(id)
})
}

View File

@ -661,7 +661,25 @@ private final class SharedApplicationContext {
return .single(nil)
}
}
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: liveLocationPolling, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
let watchTasks = self.context.get()
|> mapToSignal { context -> Signal<AccountRecordId?, NoError> in
if let context = context, let watchManager = context.context.watchManager {
let accountId = context.context.account.id
return watchManager.runningTasks
|> distinctUntilChanged
|> map { value -> AccountRecordId? in
if let value = value, value.running {
return accountId
} else {
return nil
}
}
|> distinctUntilChanged
} else {
return .single(nil)
}
}
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
return sharedContext.accountUserInterfaceInUse(id)
})
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
@ -891,6 +909,29 @@ private final class SharedApplicationContext {
}
})
}
self.mainWindow.forEachViewController({ controller in
if let controller = controller as? TabBarAccountSwitchController {
var dismissed = false
if let rootController = self.mainWindow.viewController as? TelegramRootController {
if let tabsController = rootController.viewControllers.first as? TabBarController {
for i in 0 ..< tabsController.controllers.count {
if let _ = tabsController.controllers[i] as? (SettingsController & ViewController) {
let sourceNodes = tabsController.sourceNodesForController(at: i)
if let sourceNodes = sourceNodes {
dismissed = true
controller.dismiss(sourceNodes: sourceNodes)
}
return false
}
}
}
}
if dismissed {
controller.dismiss()
}
}
return true
})
self.mainWindow.topLevelOverlayControllers = [sharedApplicationContext.overlayMediaController, context.notificationController]
var authorizeNotifications = true
if #available(iOS 10.0, *) {
@ -937,7 +978,9 @@ private final class SharedApplicationContext {
}
}))
self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context))
self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context, allowBackgroundTimeExtension: { timeout in
wakeupManager.allowBackgroundTimeExtension(timeout: timeout)
}))
let _ = self.watchCommunicationManagerPromise.get().start(next: { manager in
if let manager = manager {
watchManagerArgumentsPromise.set(.single(manager.arguments))
@ -1578,16 +1621,33 @@ private final class SharedApplicationContext {
self.openChatWhenReady(accountId: accountIdFromNotification(response.notification), peerId: peerId, messageId: messageId)
}
completionHandler()
} else if response.actionIdentifier == "reply", let peerId = peerIdFromNotification(response.notification) {
if let response = response as? UNTextInputNotificationResponse, !response.userText.isEmpty {
let text = response.userText
let signal = self.authorizedContext()
|> take(1)
|> mapToSignal { context -> Signal<Void, NoError> in
if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) {
let _ = applyMaxReadIndexInteractively(postbox: context.context.account.postbox, stateManager: context.context.account.stateManager, index: MessageIndex(id: messageId, timestamp: 0)).start()
} else if response.actionIdentifier == "reply", let peerId = peerIdFromNotification(response.notification), let accountId = accountIdFromNotification(response.notification) {
guard let response = response as? UNTextInputNotificationResponse, !response.userText.isEmpty else {
completionHandler()
return
}
let text = response.userText
let signal = self.sharedContextPromise.get()
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
return sharedContext.sharedContext.activeAccounts
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
for account in accounts {
if account.1.id == accountId {
return .single(account.1)
}
}
return enqueueMessages(account: context.context.account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)])
return .complete()
}
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { account -> Signal<Void, NoError> in
if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) {
let _ = applyMaxReadIndexInteractively(postbox: account.postbox, stateManager: account.stateManager, index: MessageIndex(id: messageId, timestamp: 0)).start()
}
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)])
|> map { messageIds -> MessageId? in
if messageIds.isEmpty {
return nil
@ -1597,7 +1657,7 @@ private final class SharedApplicationContext {
}
|> mapToSignal { messageId -> Signal<Void, NoError> in
if let messageId = messageId {
return context.context.account.postbox.unsentMessageIdsView()
return account.postbox.unsentMessageIdsView()
|> filter { view in
return !view.ids.contains(messageId)
}
@ -1610,34 +1670,20 @@ private final class SharedApplicationContext {
}
}
}
|> deliverOnMainQueue
|> timeout(15.0, queue: Queue.mainQueue(), alternate: .complete() |> beforeCompleted {
/*let content = UNMutableNotificationContent()
content.body = "Please open the app to continue sending messages"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "error"
content.userInfo = ["peerId": peerId as NSNumber]
let request = UNNotificationRequest(identifier: "reply-error", content: content, trigger: nil)
let center = UNUserNotificationCenter.current()
center.add(request)*/
})
let disposable = MetaDisposable()
disposable.set((signal
|> afterDisposed { [weak disposable] in
Queue.mainQueue().async {
if let disposable = disposable {
self.replyFromNotificationsDisposables.remove(disposable)
}
completionHandler()
}
}).start())
self.replyFromNotificationsDisposables.add(disposable)
} else {
completionHandler()
}
|> deliverOnMainQueue
let disposable = MetaDisposable()
disposable.set((signal
|> afterDisposed { [weak disposable] in
Queue.mainQueue().async {
if let disposable = disposable {
self.replyFromNotificationsDisposables.remove(disposable)
}
completionHandler()
}
}).start())
self.replyFromNotificationsDisposables.add(disposable)
} else {
completionHandler()
}

View File

@ -389,7 +389,11 @@ final class AuthorizedApplicationContext {
}*/
if let tabsController = strongSelf.rootController.viewControllers.first as? TabBarController, !tabsController.controllers.isEmpty, tabsController.selectedIndex >= 0 {
let controller = tabsController.controllers[tabsController.selectedIndex]
strongSelf.isReady.set(controller.ready.get())
let combinedReady = combineLatest(tabsController.ready.get(), controller.ready.get())
|> map { $0 && $1 }
|> filter { $0 }
|> take(1)
strongSelf.isReady.set(combinedReady)
} else {
strongSelf.isReady.set(.single(true))
}
@ -762,7 +766,8 @@ final class AuthorizedApplicationContext {
}
})
let _ = (watchManagerArguments |> deliverOnMainQueue).start(next: { [weak self] arguments in
let _ = (watchManagerArguments
|> deliverOnMainQueue).start(next: { [weak self] arguments in
guard let strongSelf = self else {
return
}

View File

@ -12,6 +12,7 @@ private struct AccountTasks {
let backgroundDownloads: Bool
let backgroundAudio: Bool
let activeCalls: Bool
let watchTasks: Bool
let userInterfaceInUse: Bool
var isEmpty: Bool {
@ -33,6 +34,9 @@ private struct AccountTasks {
if self.activeCalls {
return false
}
if self.watchTasks {
return false
}
if self.userInterfaceInUse {
return false
}
@ -57,7 +61,7 @@ final class SharedWakeupManager {
private var accountsAndTasks: [(Account, Bool, AccountTasks)] = []
init(beginBackgroundTask: @escaping (String, @escaping () -> Void) -> UIBackgroundTaskIdentifier?, endBackgroundTask: @escaping (UIBackgroundTaskIdentifier) -> Void, backgroundTimeRemaining: @escaping () -> Double, activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account)]), NoError>, liveLocationPolling: Signal<AccountRecordId?, NoError>, inForeground: Signal<Bool, NoError>, hasActiveAudioSession: Signal<Bool, NoError>, notificationManager: SharedNotificationManager?, mediaManager: MediaManager, callManager: PresentationCallManager?, accountUserInterfaceInUse: @escaping (AccountRecordId) -> Signal<Bool, NoError>) {
init(beginBackgroundTask: @escaping (String, @escaping () -> Void) -> UIBackgroundTaskIdentifier?, endBackgroundTask: @escaping (UIBackgroundTaskIdentifier) -> Void, backgroundTimeRemaining: @escaping () -> Double, activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account)]), NoError>, liveLocationPolling: Signal<AccountRecordId?, NoError>, watchTasks: Signal<AccountRecordId?, NoError>, inForeground: Signal<Bool, NoError>, hasActiveAudioSession: Signal<Bool, NoError>, notificationManager: SharedNotificationManager?, mediaManager: MediaManager, callManager: PresentationCallManager?, accountUserInterfaceInUse: @escaping (AccountRecordId) -> Signal<Bool, NoError>) {
assert(Queue.mainQueue().isCurrent())
self.beginBackgroundTask = beginBackgroundTask
@ -120,11 +124,17 @@ final class SharedWakeupManager {
}
|> distinctUntilChanged
let hasWatchTasks = watchTasks
|> map { id in
return id == account.id
}
|> distinctUntilChanged
let userInterfaceInUse = accountUserInterfaceInUse(account.id)
return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager?.isPollingState(accountId: account.id) ?? .single(false), hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, userInterfaceInUse)
|> map { importantTasksRunning, isPollingState, hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, userInterfaceInUse -> (Account, Bool, AccountTasks) in
return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: hasActiveLiveLocationPolling, backgroundDownloads: false, backgroundAudio: hasActiveAudio, activeCalls: hasActiveCalls, userInterfaceInUse: userInterfaceInUse))
return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager?.isPollingState(accountId: account.id) ?? .single(false), hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse)
|> map { importantTasksRunning, isPollingState, hasActiveAudio, hasActiveCalls, hasActiveLiveLocationPolling, hasWatchTasks, userInterfaceInUse -> (Account, Bool, AccountTasks) in
return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: hasActiveLiveLocationPolling, backgroundDownloads: false, backgroundAudio: hasActiveAudio, activeCalls: hasActiveCalls, watchTasks: hasWatchTasks, userInterfaceInUse: userInterfaceInUse))
}
}
return combineLatest(signals)

View File

@ -9,7 +9,7 @@
@property (nonatomic, readonly) bool isRunning;
- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction;
- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction allowBackgroundTimeExtension:(void (^)())allowBackgroundTimeExtension;
- (void)startRunning;
- (SSignal *)watchAppInstalledSignal;

View File

@ -49,6 +49,8 @@
NSMutableDictionary *_runningTasks;
SVariable *_hasRunningTasks;
void (^_allowBackgroundTimeExtension)();
}
@property (nonatomic, readonly) WCSession *session;
@ -57,7 +59,7 @@
@implementation TGBridgeServer
- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction
- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction allowBackgroundTimeExtension:(void (^)())allowBackgroundTimeExtension
{
self = [super init];
if (self != nil)
@ -66,6 +68,7 @@
_fileHandler = [fileHandler copy];
_dispatch = [dispatchOnQueue copy];
_logFunction = [logFunction copy];
_allowBackgroundTimeExtension = [allowBackgroundTimeExtension copy];
_runningTasks = [[NSMutableDictionary alloc] init];
_hasRunningTasks = [[SVariable alloc] init];
@ -162,6 +165,10 @@
- (void)handleMessageData:(NSData *)messageData task:(id<SDisposable>)task replyHandler:(void (^)(NSData *))replyHandler completion:(void (^)(void))completion
{
if (_allowBackgroundTimeExtension) {
_allowBackgroundTimeExtension();
}
__block id<SDisposable> runningTask = task;
void (^finishTask)(NSTimeInterval) = ^(NSTimeInterval delay)
{

View File

@ -6,6 +6,8 @@ import TelegramUI
final class WatchCommunicationManager {
private let queue: Queue
private let allowBackgroundTimeExtension: (Double) -> Void
private var server: TGBridgeServer!
private let contextDisposable = MetaDisposable()
@ -15,8 +17,9 @@ final class WatchCommunicationManager {
private let presets = Promise<WatchPresetSettings?>(nil)
private let navigateToMessagePipe = ValuePipe<MessageId>()
init(queue: Queue, context: Promise<AuthorizedApplicationContext?>) {
init(queue: Queue, context: Promise<AuthorizedApplicationContext?>, allowBackgroundTimeExtension: @escaping (Double) -> Void) {
self.queue = queue
self.allowBackgroundTimeExtension = allowBackgroundTimeExtension
let handlers = allWatchRequestHandlers.reduce([String : AnyClass]()) { (map, handler) -> [String : AnyClass] in
var map = map
@ -50,6 +53,8 @@ final class WatchCommunicationManager {
if let value = value {
Logger.shared.log("WatchBridge", value)
}
}, allowBackgroundTimeExtension: {
allowBackgroundTimeExtension(4.0)
})
self.server.startRunning()
@ -173,12 +178,12 @@ final class WatchCommunicationManager {
}
}
func watchCommunicationManager(context: Promise<AuthorizedApplicationContext?>) -> Signal<WatchCommunicationManager?, NoError> {
func watchCommunicationManager(context: Promise<AuthorizedApplicationContext?>, allowBackgroundTimeExtension: @escaping (Double) -> Void) -> Signal<WatchCommunicationManager?, NoError> {
return Signal { subscriber in
let queue = Queue()
queue.async {
if #available(iOSApplicationExtension 9.0, *) {
subscriber.putNext(WatchCommunicationManager(queue: queue, context: context))
subscriber.putNext(WatchCommunicationManager(queue: queue, context: context, allowBackgroundTimeExtension: allowBackgroundTimeExtension))
} else {
subscriber.putNext(nil)
}

@ -1 +1 @@
Subproject commit 6e0355b6326c684f465c691dbaef6d147bda38f3
Subproject commit 26b55adfb35df215fe6ec3e6a9f7c83748ec7cd5

@ -1 +1 @@
Subproject commit 8ddc0b670a139a3c37be807f7ab2e612351ff813
Subproject commit 06625eea98a27e8c41a4ef74ddeb1055b5ce036d