session-ios/Session/Meta/AppDelegate.swift
2021-10-12 17:05:13 +11:00

158 lines
6.6 KiB
Swift

import PromiseKit
import WebRTC
import SessionUIKit
import UIKit
import BackgroundTasks
import SessionUtilitiesKit
import SessionMessagingKit
extension AppDelegate {
// MARK: Call handling
@objc func setUpCallHandling() {
// Offer messages
MessageReceiver.handleOfferCallMessage = { message in
DispatchQueue.main.async {
let sdp = RTCSessionDescription(type: .offer, sdp: message.sdps![0])
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully
if let conversationVC = presentingVC as? ConversationVC, let contactThread = conversationVC.thread as? TSContactThread, contactThread.contactSessionID() == message.sender! {
let callVC = CallVC(for: message.sender!, uuid: message.uuid!, mode: .answer(sdp: sdp))
callVC.modalPresentationStyle = .overFullScreen
callVC.modalTransitionStyle = .crossDissolve
callVC.conversationVC = conversationVC
conversationVC.inputAccessoryView?.isHidden = true
conversationVC.inputAccessoryView?.alpha = 0
presentingVC.present(callVC, animated: true, completion: nil)
} else {
let incomingCallBanner = IncomingCallBanner(for: message.sender!, uuid: message.uuid!, sdp: sdp)
incomingCallBanner.show()
}
}
}
// Answer messages
MessageReceiver.handleAnswerCallMessage = { message in
DispatchQueue.main.async {
guard let callVC = CurrentAppContext().frontmostViewController() as? CallVC else { return }
callVC.handleAnswerMessage(message)
}
}
// End call messages
MessageReceiver.handleEndCallMessage = { message in
DispatchQueue.main.async {
if let currentBanner = IncomingCallBanner.current { currentBanner.dismiss() }
if let callVC = CurrentAppContext().frontmostViewController() as? CallVC { callVC.handleEndCallMessage(message) }
if let miniCallView = MiniCallView.current { miniCallView.dismiss() }
WebRTCSession.current?.dropConnection()
WebRTCSession.current = nil
}
}
}
// MARK: Configuration message
@objc(syncConfigurationIfNeeded)
func syncConfigurationIfNeeded() {
guard Storage.shared.getUser()?.name != nil else { return }
let userDefaults = UserDefaults.standard
let lastSync = userDefaults[.lastConfigurationSync] ?? .distantPast
guard Date().timeIntervalSince(lastSync) > 7 * 24 * 60 * 60,
let configurationMessage = ConfigurationMessage.getCurrent() else { return } // Sync every 2 days
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
Storage.shared.write { transaction in
let job = MessageSendJob(message: configurationMessage, destination: destination)
JobQueue.shared.add(job, using: transaction)
}
userDefaults[.lastConfigurationSync] = Date()
}
func forceSyncConfigurationNowIfNeeded() -> Promise<Void> {
guard Storage.shared.getUser()?.name != nil,
let configurationMessage = ConfigurationMessage.getCurrent() else { return Promise.value(()) }
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
let (promise, seal) = Promise<Void>.pending()
Storage.writeSync { transaction in
MessageSender.send(configurationMessage, to: destination, using: transaction).done {
seal.fulfill(())
}.catch { _ in
seal.fulfill(()) // Fulfill even if this failed; the configuration in the swarm should be at most 2 days old
}.retainUntilComplete()
}
return promise
}
// MARK: Closed group poller
@objc func startClosedGroupPoller() {
guard OWSIdentityManager.shared().identityKeyPair() != nil else { return }
ClosedGroupPoller.shared.start()
}
@objc func stopClosedGroupPoller() {
ClosedGroupPoller.shared.stop()
}
// MARK: Theme
@objc func getAppModeOrSystemDefault() -> AppMode {
let userDefaults = UserDefaults.standard
if userDefaults.dictionaryRepresentation().keys.contains("appMode") {
let mode = userDefaults.integer(forKey: "appMode")
return AppMode(rawValue: mode) ?? .light
} else {
if #available(iOS 13.0, *) {
return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light
} else {
return .light
}
}
}
// MARK: Background tasks
@available(iOS 13.0, *)
@objc func registerBackgroundTasks() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.loki-project.loki-messenger.refresh", using: nil) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.loki-project.loki-messenger.vibrate", using: nil) { task in
Vibration.shared.startVibration()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 60, execute: {
Vibration.shared.stopVibrationIfPossible()
task.setTaskCompleted(success: true)
})
}
}
@available(iOS 13.0, *)
@objc func cancelAllPendingBackgroundTasks() {
BGTaskScheduler.shared.cancelAllTaskRequests()
}
@available(iOS 13.0, *)
@objc func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.loki-project.loki-messenger.refresh")
// Fetch no earlier than 15 minutes from now.
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule app refresh: \(error)")
}
}
@available(iOS 13.0, *)
private func handleAppRefresh(task: BGAppRefreshTask) {
// Schedule a new refresh task.
scheduleAppRefresh()
AppReadiness.runNowOrWhenAppDidBecomeReady{
BackgroundPoller.poll(completionHandler: { result in
if result == .failed {
task.setTaskCompleted(success: false)
} else {
task.setTaskCompleted(success: true)
}
})
}
}
}