mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
Wakeup for watch tasks
This commit is contained in:
parent
c481383e05
commit
b2962b0dce
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user