123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- //
- // SceneDelegate.swift
- // Nextcloud
- //
- // Created by Marino Faggiana on 25/03/24.
- // Copyright © 2024 Marino Faggiana. All rights reserved.
- //
- // Author Marino Faggiana <marino.faggiana@nextcloud.com>
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- //
- import Foundation
- import UIKit
- import NextcloudKit
- import WidgetKit
- import SwiftEntryKit
- class SceneDelegate: UIResponder, UIWindowSceneDelegate {
- var window: UIWindow?
- private let appDelegate = UIApplication.shared.delegate as? AppDelegate
- private var privacyProtectionWindow: UIWindow?
- private var isFirstScene: Bool = true
- private let database = NCManageDatabase.shared
- func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
- guard let windowScene = (scene as? UIWindowScene),
- let appDelegate else { return }
- self.window = UIWindow(windowScene: windowScene)
- if let activeTableAccount = self.database.getActiveTableAccount() {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(activeTableAccount.account)")
- let capability = self.database.setCapabilities(account: activeTableAccount.account)
- NCBrandColor.shared.settingThemingColor(account: activeTableAccount.account)
- for tableAccount in self.database.getAllTableAccount() {
- NextcloudKit.shared.appendSession(account: tableAccount.account,
- urlBase: tableAccount.urlBase,
- user: tableAccount.user,
- userId: tableAccount.userId,
- password: NCKeychain().getPassword(account: tableAccount.account),
- userAgent: userAgent,
- nextcloudVersion: capability?.capabilityServerVersionMajor ?? 0,
- groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
- NCSession.shared.appendSession(account: tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId)
- }
- /// Main.storyboard
- if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
- SceneManager.shared.register(scene: scene, withRootViewController: controller)
- window?.rootViewController = controller
- window?.makeKeyAndVisible()
- /// Set the ACCOUNT
- controller.account = activeTableAccount.account
- }
- } else {
- NCKeychain().removeAll()
- if let bundleID = Bundle.main.bundleIdentifier {
- UserDefaults.standard.removePersistentDomain(forName: bundleID)
- }
- if NCBrandOptions.shared.disable_intro {
- appDelegate.openLogin(selector: NCGlobal.shared.introLogin, window: window)
- } else {
- if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() as? NCIntroViewController {
- let navigationController = NCLoginNavigationController(rootViewController: viewController)
- window?.rootViewController = navigationController
- window?.makeKeyAndVisible()
- }
- }
- }
- }
- func sceneDidDisconnect(_ scene: UIScene) {
- print("[DEBUG] Scene did disconnect")
- }
- func sceneWillEnterForeground(_ scene: UIScene) {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene will enter in foreground")
- let session = SceneManager.shared.getSession(scene: scene)
- // In Login mode is possible ONLY 1 window
- if (UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }).count > 1,
- (appDelegate?.activeLogin?.view.window != nil || appDelegate?.activeLoginWeb?.view.window != nil) || (UIApplication.shared.firstWindow?.rootViewController is NCLoginNavigationController) {
- UIApplication.shared.allSceneSessionDestructionExceptFirst()
- return
- }
- guard !session.account.isEmpty else { return }
- hidePrivacyProtectionWindow()
- if let window = SceneManager.shared.getWindow(scene: scene), let controller = SceneManager.shared.getController(scene: scene) {
- window.rootViewController = controller
- if NCKeychain().presentPasscode {
- NCPasscode.shared.presentPasscode(viewController: controller, delegate: self) {
- NCPasscode.shared.enableTouchFaceID()
- }
- } else if NCKeychain().accountRequest {
- requestedAccount(controller: controller)
- }
- }
- NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
- }
- func sceneDidBecomeActive(_ scene: UIScene) {
- let session = SceneManager.shared.getSession(scene: scene)
- let controller = SceneManager.shared.getController(scene: scene)
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene did become active")
- hidePrivacyProtectionWindow()
- NCAutoUpload.shared.initAutoUpload(controller: nil, account: session.account) { num in
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(num) uploads")
- }
- DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
- NCService().startRequestServicesServer(account: session.account, controller: controller)
- }
- DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- Task {
- await NCNetworking.shared.verifyZombie()
- }
- }
- }
- func sceneWillResignActive(_ scene: UIScene) {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene will resign active")
- NSFileProviderManager.removeAllDomains { _ in
- /*
- if !NCKeychain().disableFilesApp,
- self.database.getAllTableAccount().count > 1 {
- FileProviderDomain().registerDomains()
- }
- */
- }
- ///
- let session = SceneManager.shared.getSession(scene: scene)
- guard !session.account.isEmpty else { return }
- if NCKeychain().privacyScreenEnabled {
- if SwiftEntryKit.isCurrentlyDisplaying {
- SwiftEntryKit.dismiss {
- self.showPrivacyProtectionWindow()
- }
- } else {
- showPrivacyProtectionWindow()
- }
- }
- // Clear older files
- let days = NCKeychain().cleanUpDay
- let utilityFileSystem = NCUtilityFileSystem()
- utilityFileSystem.cleanUp(directory: utilityFileSystem.directoryProviderStorage, days: TimeInterval(days))
- }
- func sceneDidEnterBackground(_ scene: UIScene) {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene did enter in background")
- let session = SceneManager.shared.getSession(scene: scene)
- guard let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) else {
- return
- }
- if tableAccount.autoUpload {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload: true")
- if UIApplication.shared.backgroundRefreshStatus == .available {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload in background: true")
- } else {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload in background: false")
- }
- } else {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload: false")
- }
- if let error = NCAccount().updateAppsShareAccounts() {
- NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Create Apps share accounts \(error.localizedDescription)")
- }
- appDelegate?.scheduleAppRefresh()
- appDelegate?.scheduleAppProcessing()
- NCNetworking.shared.cancelAllQueue()
- if NCKeychain().presentPasscode {
- showPrivacyProtectionWindow()
- }
- }
- func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
- guard let controller = SceneManager.shared.getController(scene: scene),
- let url = URLContexts.first?.url else { return }
- let scheme = url.scheme
- let action = url.host
- let session = SceneManager.shared.getSession(scene: scene)
- guard !session.account.isEmpty else { return }
- func getMatchedAccount(userId: String, url: String) -> tableAccount? {
- if let activeTableAccount = self.database.getActiveTableAccount() {
- let urlBase = URL(string: activeTableAccount.urlBase)
- if url.contains(urlBase?.host ?? "") && userId == activeTableAccount.userId {
- return activeTableAccount
- } else {
- for tableAccount in self.database.getAllTableAccount() {
- let urlBase = URL(string: tableAccount.urlBase)
- if url.contains(urlBase?.host ?? "") && userId == tableAccount.userId {
- NCAccount().changeAccount(tableAccount.account, userProfile: nil, controller: controller) { }
- return tableAccount
- }
- }
- }
- }
- return nil
- }
- /*
- Example: nextcloud://open-action?action=create-voice-memo&&user=marinofaggiana&url=https://cloud.nextcloud.com
- */
- if scheme == NCGlobal.shared.appScheme && action == "open-action" {
- if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
- let queryItems = urlComponents.queryItems
- guard let actionScheme = queryItems?.filter({ $0.name == "action" }).first?.value,
- let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
- let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value else { return }
- if getMatchedAccount(userId: userScheme, url: urlScheme) == nil {
- let message = NSLocalizedString("_the_account_", comment: "") + " " + userScheme + NSLocalizedString("_of_", comment: "") + " " + urlScheme + " " + NSLocalizedString("_does_not_exist_", comment: "")
- let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
- controller.present(alertController, animated: true, completion: { })
- return
- }
- switch actionScheme {
- case NCGlobal.shared.actionUploadAsset:
- NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in
- if hasPermission {
- NCPhotosPickerViewController(controller: controller, maxSelectedAssets: 0, singleSelectedMode: false)
- }
- }
- case NCGlobal.shared.actionScanDocument:
- NCDocumentCamera.shared.openScannerDocument(viewController: controller)
- case NCGlobal.shared.actionTextDocument:
- let directEditingCreators = self.database.getDirectEditingCreators(account: session.account)
- let directEditingCreator = directEditingCreators!.first(where: { $0.editor == NCGlobal.shared.editorText})!
- let serverUrl = controller.currentServerUrl()
- Task {
- let fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".md", account: session.account, serverUrl: serverUrl)
- let fileNamePath = NCUtilityFileSystem().getFileNamePath(String(describing: fileName), serverUrl: serverUrl, session: session)
- NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileName), editorId: NCGlobal.shared.editorText, creatorId: directEditingCreator.identifier, templateId: NCGlobal.shared.templateDocument, account: session.account)
- }
- case NCGlobal.shared.actionVoiceMemo:
- NCAskAuthorization().askAuthorizationAudioRecord(viewController: controller) { hasPermission in
- if hasPermission {
- if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController {
- viewController.controller = controller
- viewController.modalTransitionStyle = .crossDissolve
- viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
- controller.present(viewController, animated: true, completion: nil)
- }
- }
- }
- default:
- print("No action")
- }
- }
- return
- }
- /*
- Example: nextcloud://open-file?path=Talk/IMG_0000123.jpg&user=marinofaggiana&link=https://cloud.nextcloud.com/f/123
- */
- else if scheme == NCGlobal.shared.appScheme && action == "open-file" {
- if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
- var serverUrl: String = ""
- var fileName: String = ""
- let queryItems = urlComponents.queryItems
- guard let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
- let pathScheme = queryItems?.filter({ $0.name == "path" }).first?.value,
- let linkScheme = queryItems?.filter({ $0.name == "link" }).first?.value else { return}
- guard let matchedAccount = getMatchedAccount(userId: userScheme, url: linkScheme) else {
- guard let domain = URL(string: linkScheme)?.host else { return }
- fileName = (pathScheme as NSString).lastPathComponent
- let message = String(format: NSLocalizedString("_account_not_available_", comment: ""), userScheme, domain, fileName)
- let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
- controller.present(alertController, animated: true, completion: { })
- return
- }
- let davFiles = "remote.php/dav/files/" + session.userId
- if pathScheme.contains("/") {
- fileName = (pathScheme as NSString).lastPathComponent
- serverUrl = matchedAccount.urlBase + "/" + davFiles + "/" + (pathScheme as NSString).deletingLastPathComponent
- } else {
- fileName = pathScheme
- serverUrl = matchedAccount.urlBase + "/" + davFiles
- }
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
- NCActionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: nil, fileNameOpen: fileName, sceneIdentifier: controller.sceneIdentifier)
- }
- }
- return
- /*
- Example: nextcloud://open-and-switch-account?user=marinofaggiana&url=https://cloud.nextcloud.com
- */
- } else if scheme == NCGlobal.shared.appScheme && action == "open-and-switch-account" {
- guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
- let queryItems = urlComponents.queryItems
- guard let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
- let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value else { return }
- // If the account doesn't exist, return false which will open the app without switching
- if getMatchedAccount(userId: userScheme, url: urlScheme) == nil {
- return
- }
- // Otherwise open the app and switch accounts
- return
- } else if let action {
- if DeepLink(rawValue: action) != nil {
- NCDeepLinkHandler().parseDeepLink(url, controller: controller)
- }
- return
- } else {
- let applicationHandle = NCApplicationHandle()
- let isHandled = applicationHandle.applicationOpenURL(url)
- if isHandled {
- return
- } else {
- scene.open(url, options: nil)
- }
- }
- }
- private func showPrivacyProtectionWindow() {
- guard let windowScene = self.window?.windowScene else {
- return
- }
- privacyProtectionWindow = UIWindow(windowScene: windowScene)
- privacyProtectionWindow?.rootViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()
- privacyProtectionWindow?.windowLevel = .alert + 1
- privacyProtectionWindow?.makeKeyAndVisible()
- }
- private func hidePrivacyProtectionWindow() {
- privacyProtectionWindow?.isHidden = true
- privacyProtectionWindow = nil
- }
- }
- // MARK: - Extension
- extension SceneDelegate: NCPasscodeDelegate {
- func requestedAccount(controller: UIViewController?) {
- let tableAccounts = self.database.getAllTableAccount()
- if tableAccounts.count > 1, let accountRequestVC = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
- accountRequestVC.controller = controller
- accountRequestVC.activeAccount = (controller as? NCMainTabBarController)?.account
- accountRequestVC.accounts = tableAccounts
- accountRequestVC.enableTimerProgress = true
- accountRequestVC.enableAddAccount = false
- accountRequestVC.dismissDidEnterBackground = false
- accountRequestVC.delegate = self
- accountRequestVC.startTimer()
- let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height / 5)
- let numberCell = tableAccounts.count
- let height = min(CGFloat(numberCell * Int(accountRequestVC.heightCell) + 45), screenHeighMax)
- let popup = NCPopupViewController(contentController: accountRequestVC, popupWidth: 300, popupHeight: height + 20)
- popup.backgroundAlpha = 0.8
- controller?.present(popup, animated: true)
- }
- }
- func passcodeReset(_ passcodeViewController: TOPasscodeViewController) {
- appDelegate?.resetApplication()
- }
- }
- extension SceneDelegate: NCAccountRequestDelegate {
- func accountRequestAddAccount() { }
- func accountRequestChangeAccount(account: String, controller: UIViewController?) {
- NCAccount().changeAccount(account, userProfile: nil, controller: controller as? NCMainTabBarController) { }
- }
- }
- // MARK: - Scene Manager
- class SceneManager {
- static let shared = SceneManager()
- private var sceneController: [NCMainTabBarController: UIScene] = [:]
- func register(scene: UIScene, withRootViewController rootViewController: NCMainTabBarController) {
- sceneController[rootViewController] = scene
- }
- func getController(scene: UIScene?) -> NCMainTabBarController? {
- for controller in sceneController.keys {
- if sceneController[controller] == scene {
- return controller
- }
- }
- return nil
- }
- func getController(sceneIdentifier: String?) -> NCMainTabBarController? {
- if let sceneIdentifier {
- for controller in sceneController.keys {
- if sceneIdentifier == controller.sceneIdentifier {
- return controller
- }
- }
- }
- return nil
- }
- func getControllers() -> [NCMainTabBarController] {
- return Array(sceneController.keys)
- }
- func getWindow(scene: UIScene?) -> UIWindow? {
- return (scene as? UIWindowScene)?.keyWindow
- }
- func getWindow(controller: NCMainTabBarController?) -> UIWindow? {
- guard let controller,
- let scene = sceneController[controller] else { return nil }
- return getWindow(scene: scene)
- }
- func getSceneIdentifier() -> [String] {
- var results: [String] = []
- for controller in sceneController.keys {
- results.append(controller.sceneIdentifier)
- }
- return results
- }
- func getSession(scene: UIScene?) -> NCSession.Session {
- let controller = SceneManager.shared.getController(scene: scene)
- return NCSession.shared.getSession(controller: controller)
- }
- }
|