From d5ea0c3f7a4be0ea78a00f02f9d4447444e44a73 Mon Sep 17 00:00:00 2001 From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com> Date: Tue, 17 Oct 2023 18:03:55 +0530 Subject: [PATCH 1/5] NMC 1971 - Launch screen and Intro screen UI Customisation --- Brand/Intro/NCIntro.storyboard | 134 +++++++-------- Brand/Intro/NCIntroCollectionViewCell.xib | 43 ++--- Brand/Intro/NCIntroViewController.swift | 83 +++++++-- Brand/LaunchScreen.storyboard | 33 +++- .../OnboardingTestCase.swift | 158 ++++++++++++++++++ iOSClient/AppDelegate.swift | 9 + iOSClient/AppUtility.swift | 21 +++ 7 files changed, 362 insertions(+), 119 deletions(-) create mode 100644 Tests/NextcloudUnitTests/OnboardingTestCase.swift create mode 100644 iOSClient/AppUtility.swift diff --git a/Brand/Intro/NCIntro.storyboard b/Brand/Intro/NCIntro.storyboard index 1a4fabd425..c17b6df339 100644 --- a/Brand/Intro/NCIntro.storyboard +++ b/Brand/Intro/NCIntro.storyboard @@ -1,137 +1,123 @@ - + - + - + - - + + - - + + - - - - - + - - + + - - + - - - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - + - + - + diff --git a/Brand/Intro/NCIntroCollectionViewCell.xib b/Brand/Intro/NCIntroCollectionViewCell.xib index 7eed66d3ba..ab7c53a1de 100644 --- a/Brand/Intro/NCIntroCollectionViewCell.xib +++ b/Brand/Intro/NCIntroCollectionViewCell.xib @@ -1,56 +1,57 @@ - + - + - + - - + - - - - - - + + + + + + + + - - + + - + diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index d340729b8b..532a6c738b 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -4,6 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import UIKit +import NextcloudKit class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { @IBOutlet weak var buttonLogin: UIButton! @@ -11,23 +12,31 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol @IBOutlet weak var buttonHost: UIButton! @IBOutlet weak var introCollectionView: UICollectionView! @IBOutlet weak var pageControl: UIPageControl! + @IBOutlet weak var contstraintBottomLoginButton: NSLayoutConstraint! weak var delegate: NCIntroViewController? // Controller var controller: NCMainTabBarController? private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! - private let titles = [NSLocalizedString("_intro_1_title_", comment: ""), NSLocalizedString("_intro_2_title_", comment: ""), NSLocalizedString("_intro_3_title_", comment: ""), NSLocalizedString("_intro_4_title_", comment: "")] - private let images = [UIImage(named: "intro1"), UIImage(named: "intro2"), UIImage(named: "intro3"), UIImage(named: "intro4")] - private var timer: Timer? + private let titles = [NSLocalizedString("", comment: ""), NSLocalizedString("", comment: ""), NSLocalizedString("", comment: "")] + private var images:[UIImage?] = [] + private var timerAutoScroll: Timer? + private var textColor: UIColor = .white private var textColorOpponent: UIColor = .black private var activeLoginProvider: NCLoginProvider? + private let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")] + private let imagesPortrait = [UIImage(named: "introSlide1"), UIImage(named: "introSlide2"), UIImage(named: "introSlide3")] + private let imagesEightPortrait = [UIImage(named: "introSlideEight1"), UIImage(named: "introSlideEight2"), UIImage(named: "introSlideEight3")] // MARK: - View Life Cycle override func viewDidLoad() { super.viewDidLoad() + + let isEightPlusDevice = UIScreen.main.bounds.height == 736 + images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) let isTooLight = NCBrandColor.shared.customer.isTooLight() let isTooDark = NCBrandColor.shared.customer.isTooDark() @@ -83,7 +92,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol view.backgroundColor = NCBrandColor.shared.customer - self.timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(self.autoScroll(_:))), userInfo: nil, repeats: true) + timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true) + NotificationCenter.default.addObserver(self, selector: #selector(resetPageController(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) } override var preferredStatusBarStyle: UIStatusBarStyle { @@ -94,20 +104,38 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if (UIDevice.current.userInterfaceIdiom != .pad){ + AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) + } + navigationController?.setNavigationBarHidden(true, animated: animated) + } + + override func viewDidLayoutSubviews() { + if UIScreen.main.bounds.width < 350 || UIScreen.main.bounds.height > 800 { + contstraintBottomLoginButton.constant = 15 + } + } + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - - timer?.invalidate() - timer = nil + timerAutoScroll?.invalidate() + AppUtility.lockOrientation(UIInterfaceOrientationMask.all) + navigationController?.setNavigationBarHidden(false, animated: animated) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - - coordinator.animate(alongsideTransition: nil) { _ in - self.pageControl?.currentPage = 0 - self.introCollectionView?.collectionViewLayout.invalidateLayout() - } + let isEightPlusDevice = UIScreen.main.bounds.height == 736 + images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) + pageControl.currentPage = 0 + introCollectionView.collectionViewLayout.invalidateLayout() + self.introCollectionView.reloadData() + } + + @objc func resetPageController(_ notification: NSNotification){ + pageControl.currentPage = 0 + introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: true) } @objc func autoScroll(_ sender: Any?) { @@ -134,6 +162,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol cell.titleLabel.textColor = textColor cell.titleLabel.text = titles[indexPath.row] cell.imageView.image = images[indexPath.row] + cell.imageView.contentMode = .scaleAspectFill return cell } @@ -142,8 +171,14 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(autoScroll(_:))), userInfo: nil, repeats: true) - pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) + timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true) + let page = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) + if pageControl.currentPage == (images.count - 1), pageControl.currentPage <= page { + pageControl.currentPage = 0 + introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: false) + } else { + pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) + } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { @@ -158,11 +193,23 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } @IBAction func login(_ sender: Any) { - if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin { - viewController.controller = self.controller - self.navigationController?.pushViewController(viewController, animated: true) + if NCBrandOptions.shared.use_AppConfig { + let loginViewPage = UIStoryboard(name: "NCLogin", bundle: Bundle.main).instantiateViewController(identifier: "NCLogin") + navigationController?.pushViewController(loginViewPage, animated: true) + } else { + if NextcloudKit.shared.isNetworkReachable() { + appDelegate.openLogin(selector: NCGlobal.shared.introLogin) + } else { + showNoInternetAlert() + } } } + + func showNoInternetAlert(){ + let alertController = UIAlertController(title: NSLocalizedString("_no_internet_alert_title_", comment: ""), message: NSLocalizedString("_no_internet_alert_message_", comment: ""), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { action in })) + self.present(alertController, animated: true) + } @IBAction func signupWithProvider(_ sender: Any) { let loginProvider = NCLoginProvider() diff --git a/Brand/LaunchScreen.storyboard b/Brand/LaunchScreen.storyboard index 26840f6195..29059fe902 100755 --- a/Brand/LaunchScreen.storyboard +++ b/Brand/LaunchScreen.storyboard @@ -1,9 +1,9 @@ - + - + @@ -17,16 +17,37 @@ - + + + + + + + + + + + + - - + - + + diff --git a/Tests/NextcloudUnitTests/OnboardingTestCase.swift b/Tests/NextcloudUnitTests/OnboardingTestCase.swift new file mode 100644 index 0000000000..161b3a9258 --- /dev/null +++ b/Tests/NextcloudUnitTests/OnboardingTestCase.swift @@ -0,0 +1,158 @@ +// +// OnboardingTestCase.swift +// NextcloudTests +// +// Created by A200073704 on 21/04/23. +// Copyright © 2023 Marino Faggiana. All rights reserved. +// + +@testable import Nextcloud +import XCTest +import NextcloudKit + + class OnboardingTestCase: XCTestCase { + + var viewController = NCIntroViewController() + + + var images:[UIImage?] = [] + let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")] + let imagesPortrait = [UIImage(named: "introSlide1"), UIImage(named: "introSlide2"), UIImage(named: "introSlide3")] + let imagesEightPortrait = [UIImage(named: "introSlideEight1"), UIImage(named: "introSlideEight2"), UIImage(named: "introSlideEight3")] + + + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + + func testValidImage() { + + // onscreen images should not be nill + let image = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")] + XCTAssertNotNil(image, "Image should not be nil") + + } + + func testImageDimensionsLandscape() { + + // testing height and width of the image + let introCollectionView = UIImage(named: "introSlideLand1") + XCTAssertEqual(introCollectionView?.size.width, 390, "Image width should be 390") + XCTAssertEqual(introCollectionView?.size.height, 844.3333333333334, "Image height should be 844.3333333333334") + } + + func testImageDimensionsPortrait() { + + // testing height and width of the image + let introCollectionView = UIImage(named: "introSlide1") + + XCTAssertEqual(introCollectionView?.size.width, 390, "Image width should be 390") + XCTAssertEqual(introCollectionView?.size.height, 844.3333333333334, "Image height should be 844.3333333333334") + } + + + func testImageDimentionsNotEqual() { + + // testing width and height if not equal + let introCollectionView = UIImage(named: "introSlide2") + + XCTAssertNotEqual(introCollectionView?.size.width, 100, "Image width should be 390") + XCTAssertNotEqual(introCollectionView?.size.height, 820, "Image height should be 844.3333333333334") + + } + + + func testImageContentMode() { + + // imageview content mode should be scaleAspectFill + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFill + imageView.image = UIImage(named: "introSlideLand2") + XCTAssertEqual(imageView.contentMode, .scaleAspectFill, "Image content mode should be scaleAspectFill") + + } + + + // Background color of view should be customer + func testBackgroundcolor() { + + let backgroundColor = NCBrandColor.shared.customer + XCTAssertNotNil(backgroundColor, "NCBrandColor.shared.customer should not be nil") + + } + + + // Button login text color shouyld be white + func testButtonLoginTextColor() { + + let textColor: UIColor = .white + viewController.buttonLogin?.backgroundColor = textColor + + XCTAssertEqual(textColor, textColor) + + } + + // images at loginscreen should not be empty + func testImagesNotEmpty() { + + let isEightPlusDevice = UIScreen.main.bounds.height == 736 + images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) + + XCTAssertFalse(images.isEmpty) + } + + + // Status bar and navigation bar color should not be blue color + func testStatueBarColorNotEqualToCustomer() { + + + let view = NCLoginWeb() + var color = view.navigationController?.navigationBar.backgroundColor + let navigationBarColor: UIColor = NCBrandColor.shared.customer + color = .systemBlue + + XCTAssertNotEqual(navigationBarColor, color) + + } + + //NavigationBar and status Bar color should be equal + func testNavigationBarColorEqualToCustomer() { + + let statusBarColor = NCBrandColor.shared.customer + let navigationBarColor: UIColor = NCBrandColor.shared.customer + + XCTAssertEqual(navigationBarColor, statusBarColor) + } + + func testEightPlusDeviceHeight() { + + let eightPlusDevice = UIScreen.main.bounds.height >= 736 + + XCTAssertTrue(eightPlusDevice) + + } + + func testLoginButtonTapped() { + + let viewController = NCIntroViewController() + + let loginButton = UIButton() + loginButton.addTarget(nil, action: #selector(viewController.login(_:)), for: .touchUpInside) + loginButton.sendActions(for: .touchUpInside) + + viewController.login(loginButton) + + XCTAssertNotNil(loginButton) + } + + + + +} diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 133431af35..2ab0275494 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -16,6 +16,8 @@ import RealmSwift class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var backgroundSessionCompletionHandler: (() -> Void)? + var taskAutoUploadDate: Date = Date() + var orientationLock = UIInterfaceOrientationMask.all var isUiTestingEnabled: Bool { return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } @@ -324,3 +326,10 @@ extension AppDelegate: NCCreateFormUploadConflictDelegate { } } } + +//MARK: NMC Customisation +extension AppDelegate { + func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { + return self.orientationLock + } +} diff --git a/iOSClient/AppUtility.swift b/iOSClient/AppUtility.swift new file mode 100644 index 0000000000..bb7c625e53 --- /dev/null +++ b/iOSClient/AppUtility.swift @@ -0,0 +1,21 @@ +// +// AppUtility.swift +// Nextcloud +// +// Created by Amrut Waghmare on 17/10/23. +// Copyright © 2023 Marino Faggiana. All rights reserved. +// + +import Foundation +struct AppUtility { + static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { + if let delegate = UIApplication.shared.delegate as? AppDelegate { + delegate.orientationLock = orientation + } + } + + static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { + self.lockOrientation(orientation) + UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") + } +} From 37cc49e3cfa8678e84ce525c7e29bc6240f1577d Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Wed, 9 Apr 2025 12:11:56 +0530 Subject: [PATCH 2/5] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 3 +- Nextcloud.xcodeproj/project.pbxproj | 12 ++ iOSClient/AppDelegate.swift | 193 +++++++++++++++++++++++- 3 files changed, 206 insertions(+), 2 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index 532a6c738b..cdddc74d94 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -198,7 +198,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol navigationController?.pushViewController(loginViewPage, animated: true) } else { if NextcloudKit.shared.isNetworkReachable() { - appDelegate.openLogin(selector: NCGlobal.shared.introLogin) +// appDelegate.openLogin(selector: NCGlobal.shared.introLogin) + appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false) } else { showNoInternetAlert() } diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index b24d42ac75..38301c59d5 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -90,6 +90,11 @@ AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; }; AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; }; CB3666201AF7550816B5CD6A /* NCContextMenuComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8932E90EC4278026D86CCCC9 /* NCContextMenuComment.swift */; }; + AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; }; + B54315322DA64BAF00981E7E /* OnboardingTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */; }; + B54315342DA64D6500981E7E /* AppUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315332DA64D6500981E7E /* AppUtility.swift */; }; + C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; }; + D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; }; F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; }; F31165022F9674A1009A1E37 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = F31165012F9674A1009A1E37 /* AppIcon.icon */; }; @@ -1280,6 +1285,9 @@ AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; }; B4C7A5B36D1ED178FB6B76CB /* NCContextMenuPlayerTracks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenuPlayerTracks.swift; sourceTree = ""; }; BB7697C94BA14450A0867940 /* NCContextMenuProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenuProfile.swift; sourceTree = ""; }; + AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; }; + B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTestCase.swift; sourceTree = ""; }; + B54315332DA64D6500981E7E /* AppUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUtility.swift; sourceTree = ""; }; C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; }; @@ -2104,6 +2112,8 @@ children = ( F34BDB3B2F574A58007A222C /* BidiSafeFilenameTests.swift */, AA52EB452D42AC5A0089C348 /* Placeholder.swift */, + B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */, + AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */, ); path = NextcloudUnitTests; sourceTree = ""; @@ -3351,6 +3361,7 @@ isa = PBXGroup; children = ( AA517BB42D66149900F8D37C /* .tx */, + B54315332DA64D6500981E7E /* AppUtility.swift */, F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */, F7110ADF2F9773210095AA5C /* AppDelegate+AppRefresh.swift */, F7110AE32F9774130095AA5C /* AppDelegate+AppProcessing.swift */, @@ -4782,6 +4793,7 @@ F7FA80002C0F4F3B0072FC60 /* NCUploadAssetsModel.swift in Sources */, F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */, F73EF7DF2B02266D0087E6E9 /* NCManageDatabase+Trash.swift in Sources */, + B54315342DA64D6500981E7E /* AppUtility.swift in Sources */, F79B646026CA661600838ACA /* UIControl+Extension.swift in Sources */, F768823E2C0DD305001CF441 /* LazyView.swift in Sources */, F7CAFE1B2F16AA8D00DB35A5 /* main.swift in Sources */, diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 2ab0275494..b0db5679bc 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -16,8 +16,11 @@ import RealmSwift class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var backgroundSessionCompletionHandler: (() -> Void)? + var activeLogin: NCLogin? + var activeLoginWeb: NCLoginWeb? var taskAutoUploadDate: Date = Date() var orientationLock = UIInterfaceOrientationMask.all + @objc let adjust = AdjustHelper() var isUiTestingEnabled: Bool { return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } @@ -33,6 +36,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var bgTask: UIBackgroundTaskIdentifier = .invalid var pushSubscriptionTask: Task? + var window: UIWindow? + @objc var sceneIdentifier: String = "" + @objc var activeViewController: UIViewController? + @objc var account: String = "" + @objc var urlBase: String = "" + @objc var user: String = "" + @objc var userId: String = "" + @objc var password: String = "" + var timerErrorNetworking: Timer? + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if isUiTestingEnabled { Task { @@ -58,6 +71,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } NCBrandColor.shared.createUserColors() + NCImageCache.shared.createImagesCache() // Setup Networking // @@ -117,6 +131,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NCPreferences().requestPasscodeAtStart = true } + /// Activation singleton + _ = NCAppStateManager.shared + _ = NCNetworking.shared + _ = NCDownloadAction.shared + _ = NCNetworkingProcess.shared + + adjust.configAdjust() + adjust.subsessionStart() + TealiumHelper.shared.start() + FirebaseApp.configure() + return true } @@ -247,6 +272,151 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } + // MARK: - Login + + func openLogin(selector: Int, window: UIWindow? = nil) { + UIApplication.shared.allSceneSessionDestructionExceptFirst() + + // Nextcloud standard login + if selector == NCGlobal.shared.introSignup { + if activeLogin?.view.window == nil { + if selector == NCGlobal.shared.introSignup { + let web = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider + web?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders + showLoginViewController(web) + } else { + activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin + if let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController, !controller.account.isEmpty { + let session = NCSession.shared.getSession(account: controller.account) + activeLogin?.urlBase = session.urlBase + } + showLoginViewController(activeLogin) + } + } + } else { + if activeLogin?.view.window == nil { + activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin + activeLogin?.urlBase = NCBrandOptions.shared.disable_request_login_url ? NCBrandOptions.shared.loginBaseUrl : "" + showLoginViewController(activeLogin) + } + } + } + + @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) { +// openLogin(selector: NCGlobal.shared.introLogin) + // [WEBPersonalized] [AppConfig] + if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig { + + if activeLoginWeb?.view.window == nil { + activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb + activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl + showLoginViewController(activeLoginWeb, contextViewController: viewController) + } + return + } + + // Nextcloud standard login + if selector == NCGlobal.shared.introSignup { + + if activeLoginWeb?.view.window == nil { + activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb + if selector == NCGlobal.shared.introSignup { + activeLoginWeb?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders + } else { + activeLoginWeb?.urlBase = self.urlBase + } + showLoginViewController(activeLoginWeb, contextViewController: viewController) + } + + } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url { + + if activeLoginWeb?.view.window == nil { + activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb + activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl + showLoginViewController(activeLoginWeb, contextViewController: viewController) + } + + } else if openLoginWeb { + + // Used also for reinsert the account (change passwd) + if activeLoginWeb?.view.window == nil { + activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb + activeLoginWeb?.urlBase = urlBase + activeLoginWeb?.user = user + showLoginViewController(activeLoginWeb, contextViewController: viewController) + } + + } else { + + if activeLogin?.view.window == nil { + activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin + showLoginViewController(activeLogin, contextViewController: viewController) + } + } + } + + func showLoginViewController(_ viewController: UIViewController?) { + guard let viewController else { return } + let navigationController = NCLoginNavigationController(rootViewController: viewController) + + navigationController.modalPresentationStyle = .fullScreen + navigationController.navigationBar.barStyle = .black + navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText + navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer + navigationController.navigationBar.isTranslucent = false + + if let controller = UIApplication.shared.firstWindow?.rootViewController { + if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) { + presentedVC.dismiss(animated: false) { + controller.present(navigationController, animated: true) + } + } else { + controller.present(navigationController, animated: true) + } + } else { + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + } + } + + func showLoginViewController(_ viewController: UIViewController?, contextViewController: UIViewController?) { + + if contextViewController == nil { + if let viewController = viewController { + let navigationController = NCLoginNavigationController(rootViewController: viewController) + navigationController.navigationBar.barStyle = .black + navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText + navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer + navigationController.navigationBar.isTranslucent = false + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + } + } else if contextViewController is UINavigationController { + if let contextViewController = contextViewController, let viewController = viewController { + (contextViewController as? UINavigationController)?.pushViewController(viewController, animated: true) + } + } else { + if let viewController = viewController, let contextViewController = contextViewController { + let navigationController = NCLoginNavigationController(rootViewController: viewController) + navigationController.modalPresentationStyle = .fullScreen + navigationController.navigationBar.barStyle = .black + navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText + navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer + navigationController.navigationBar.isTranslucent = false + contextViewController.present(navigationController, animated: true) { } + } + } + } + + @objc func startTimerErrorNetworking() { + timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true) + } + + @objc private func checkErrorNetworking() { + guard !account.isEmpty, NCKeychain().getPassword(account: account).isEmpty else { return } + openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true) + } + // MARK: - func trustCertificateError(host: String) { @@ -283,9 +453,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true) } + // MARK: - Account + + @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) { +// NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser) + } + + @objc func deleteAccount(_ account: String, wipe: Bool) { + NCAccount().deleteAccount(account, wipe: wipe) + } + + func deleteAllAccounts() { + let accounts = NCManageDatabase.shared.getAccounts() + accounts?.forEach({ account in + deleteAccount(account, wipe: true) + }) + } + + func updateShareAccounts() -> Error? { + return NCAccount().updateAppsShareAccounts() + } + // MARK: - Reset Application - func resetApplication() { + @objc func resetApplication() { let utilityFileSystem = NCUtilityFileSystem() NCNetworking.shared.cancelAllTask() From 857466fbb6399bc2913e79cfa15531dab1090924 Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Tue, 30 Sep 2025 21:27:11 +0530 Subject: [PATCH 3/5] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 18 +- Brand/LaunchScreen.storyboard | 2 +- iOSClient/AppDelegate.swift | 330 ++++++++++++++++++++---- 3 files changed, 293 insertions(+), 57 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index cdddc74d94..226e6f3318 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -61,7 +61,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol self.navigationController?.navigationBar.tintColor = textColor if !NCManageDatabase.shared.getAllTableAccount().isEmpty { - let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(actionCancel(_:))) + let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(self.actionCancel)) navigationItemCancel.tintColor = textColor navigationItem.leftBarButtonItem = navigationItemCancel } @@ -74,10 +74,13 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol buttonLogin.backgroundColor = textColor buttonLogin.setTitle(NSLocalizedString("_log_in_", comment: ""), for: .normal) - buttonSignUp.layer.cornerRadius = 8 + buttonSignUp.layer.cornerRadius = 20 + buttonSignUp.layer.borderColor = textColor.cgColor + buttonSignUp.layer.borderWidth = 1.0 buttonSignUp.setTitleColor(textColor, for: .normal) - buttonSignUp.backgroundColor = textColor.withAlphaComponent(0.2) + buttonSignUp.backgroundColor = NCBrandColor.shared.customer buttonSignUp.titleLabel?.adjustsFontSizeToFitWidth = true + buttonSignUp.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) buttonSignUp.setTitle(NSLocalizedString("_sign_up_", comment: ""), for: .normal) buttonHost.layer.cornerRadius = 20 @@ -138,7 +141,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: true) } - @objc func autoScroll(_ sender: Any?) { + @objc func autoScroll() { if pageControl.currentPage + 1 >= titles.count { pageControl.currentPage = 0 } else { @@ -182,13 +185,13 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - timer?.invalidate() - timer = nil + timerAutoScroll?.invalidate() + timerAutoScroll = nil } // MARK: - Action - @objc func actionCancel(_ sender: Any?) { + @objc func actionCancel() { dismiss(animated: true) { } } @@ -198,7 +201,6 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol navigationController?.pushViewController(loginViewPage, animated: true) } else { if NextcloudKit.shared.isNetworkReachable() { -// appDelegate.openLogin(selector: NCGlobal.shared.introLogin) appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false) } else { showNoInternetAlert() diff --git a/Brand/LaunchScreen.storyboard b/Brand/LaunchScreen.storyboard index 29059fe902..59fe1c3dd1 100755 --- a/Brand/LaunchScreen.storyboard +++ b/Brand/LaunchScreen.storyboard @@ -42,7 +42,7 @@ - + diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index b0db5679bc..304429582e 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -12,7 +12,7 @@ import WidgetKit import Queuer import EasyTipView import SwiftUI -import RealmSwift +import MoEngageInApps class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var backgroundSessionCompletionHandler: (() -> Void)? @@ -25,6 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } var notificationSettings: UNNotificationSettings? + var pushKitToken: String? var loginFlowV2Token = "" var loginFlowV2Endpoint = "" @@ -36,6 +37,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var bgTask: UIBackgroundTaskIdentifier = .invalid var pushSubscriptionTask: Task? + let database = NCManageDatabase.shared + var window: UIWindow? @objc var sceneIdentifier: String = "" @objc var activeViewController: UIViewController? @@ -45,21 +48,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD @objc var userId: String = "" @objc var password: String = "" var timerErrorNetworking: Timer? - + var tipView: EasyTipView? + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if isUiTestingEnabled { - Task { - await NCAccount().deleteAllAccounts() - } + NCAccount().deleteAllAccounts() } + UINavigationBar.appearance().tintColor = NCBrandColor.shared.customer + UIToolbar.appearance().tintColor = NCBrandColor.shared.customer + let utilityFileSystem = NCUtilityFileSystem() let utility = NCUtility() - - utilityFileSystem.createDirectoryStandard() - utilityFileSystem.emptyTemporaryDirectory() - utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") - - let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionBuild()) + let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp()) NCAppVersionManager.shared.checkAndUpdateInstallState() NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0) @@ -69,6 +69,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { FirebaseApp.configure() } + utilityFileSystem.createDirectoryStandard() + utilityFileSystem.emptyTemporaryDirectory() + utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") NCBrandColor.shared.createUserColors() NCImageCache.shared.createImagesCache() @@ -79,23 +82,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD delegate: NCNetworking.shared) NCNetworking.shared.setupTransferDelegate() - NextcloudKit.configureLogger(logLevel: (NCBrandOptions.shared.disable_log ? .disabled : NCPreferences().log)) - - #if DEBUG -// For the tags look NCGlobal LOG TAG - -// var black: [String] = [] -// black.append("NETWORKING TASKS") -// NextcloudKit.configureLoggerBlacklist(blacklist: black) - -// var white: [String] = [] -// white.append("SYNC METADATA") -// NextcloudKit.configureLoggerWhitelist(whitelist: white) - #endif - - nkLog(start: "Start session with level \(NCPreferences().log) " + versionNextcloudiOS) + if NCBrandOptions.shared.disable_log { + utilityFileSystem.removeFile(atPath: NextcloudKit.shared.nkCommonInstance.filenamePathLog) + utilityFileSystem.removeFile(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NextcloudKit.shared.nkCommonInstance.filenameLog) + } else { + NextcloudKit.shared.setupLog(pathLog: utilityFileSystem.directoryGroup, + levelLog: NCKeychain().logLevel, + copyLogToDocumentDirectory: true) + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(NCKeychain().logLevel) " + versionNextcloudiOS) + } - // Push Notification & display notification + /// Push Notification & display notification UNUserNotificationCenter.current().getNotificationSettings { settings in self.notificationSettings = settings } @@ -116,32 +113,45 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } self.handleAppRefresh(appRefreshTask) } - scheduleAppRefresh() - - BGTaskScheduler.shared.register(forTaskWithIdentifier: global.processingTask, using: backgroundQueue) { task in - guard let processingTask = task as? BGProcessingTask else { - task.setTaskCompleted(success: false) - return - } - self.handleProcessingTask(processingTask) + BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in + self.handleProcessingTask(task) } - scheduleAppProcessing() if NCBrandOptions.shared.enforce_passcode_lock { - NCPreferences().requestPasscodeAtStart = true + NCKeychain().requestPasscodeAtStart = true } /// Activation singleton - _ = NCAppStateManager.shared _ = NCNetworking.shared - _ = NCDownloadAction.shared + _ = NCActionCenter.shared _ = NCNetworkingProcess.shared + _ = NCTransferProgress.shared + _ = NCActionCenter.shared + NCTransferProgress.shared.setup() + NCActionCenter.shared.setup() + +// if account.isEmpty { +// if NCBrandOptions.shared.disable_intro { +// openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false) +// } else { +// if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() { +// let navigationController = NCLoginNavigationController(rootViewController: viewController) +// window?.rootViewController = navigationController +// window?.makeKeyAndVisible() +// } +// } +// } else { +// NCPasscode.shared.presentPasscode(delegate: self) { +// NCPasscode.shared.enableTouchFaceID() +// } +// } adjust.configAdjust() adjust.subsessionStart() TealiumHelper.shared.start() FirebaseApp.configure() + return true } @@ -172,6 +182,198 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } + // MARK: - Background Task + + /* + @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage. + */ + func scheduleAppRefresh() { + let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask) + + request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds. + do { + try BGTaskScheduler.shared.submit(request) + } catch { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)") + } + } + + /* + @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week. + */ + func scheduleAppProcessing() { + let request = BGProcessingTaskRequest(identifier: NCGlobal.shared.processingTask) + + request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes. + request.requiresNetworkConnectivity = false + request.requiresExternalPower = false + do { + try BGTaskScheduler.shared.submit(request) + } catch { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)") + } + } + + func handleAppRefresh(_ task: BGTask) { + scheduleAppRefresh() + + Task { + defer { + task.setTaskCompleted(success: true) + } + + await backgroundSync(task: task) + } + } + + func handleProcessingTask(_ task: BGProcessingTask) { + nkLog(tag: self.global.logTagTask, emoji: .start, message: "Start processing task") + guard NCManageDatabase.shared.openRealmBackground() else { + nkLog(tag: self.global.logTagTask, emoji: .error, message: "Failed to open Realm in background") + task.setTaskCompleted(success: false) + return + } + var expired = false + task.expirationHandler = { + expired = true + } + + // Schedule next processing task + scheduleAppProcessing() + + Task { + defer { + task.setTaskCompleted(success: true) + } + + // If possible, cleaning every week + if NCPreferences().cleaningWeek() { + // BGTask expiration flag + nkLog(tag: self.global.logTagBgSync, emoji: .start, message: "Start cleaning week") + let tblAccounts = await NCManageDatabase.shared.getAllTableAccountAsync() + for tblAccount in tblAccounts { + await NCManageDatabase.shared.cleanTablesOcIds(account: tblAccount.account, userId: tblAccount.userId, urlBase: tblAccount.urlBase) + guard !expired else { return } + } + await NCUtilityFileSystem().cleanUpAsync() + + NCPreferences().setDoneCleaningWeek() + nkLog(tag: self.global.logTagBgSync, emoji: .stop, message: "Stop cleaning week") + } else { + await backgroundSync(task: task) + } + } + } + + func backgroundSync(task: BGTask? = nil) async { + defer { + // Update badge safely at the end of the background sync + Task { @MainActor in + do { + let count = await NCManageDatabase.shared.getMetadatasInWaitingCountAsync() + try await UNUserNotificationCenter.current().setBadgeCount(count) + } catch { } + } + } + + // BGTask expiration flag + var expired = false + task?.expirationHandler = { + expired = true + } + + // Discover new items for Auto Upload + let numAutoUpload = await NCAutoUpload.shared.initAutoUpload() + nkLog(tag: self.global.logTagBgSync, emoji: .start, message: "Auto upload found \(numAutoUpload) new items") + guard !expired else { return } + + // Fetch METADATAS + let metadatas = await NCManageDatabase.shared.getMetadataProcess() + guard !metadatas.isEmpty, !expired else { + return + } + + // Create all pending Auto Upload folders (fail-fast) + let pendingCreateFolders = metadatas.lazy.filter { + $0.status == self.global.metadataStatusWaitCreateFolder && + $0.sessionSelector == self.global.selectorUploadAutoUpload + } + + for metadata in pendingCreateFolders { + guard !expired else { return } + + let err = await NCNetworking.shared.createFolderForAutoUpload( + serverUrlFileName: metadata.serverUrlFileName, + account: metadata.account + ) + // Fail-fast: abort the whole sync on first failure + if err != .success { + nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Create folder '\(metadata.serverUrlFileName)' failed: \(err.errorCode) – aborting sync") + return + } + } + + // Capacity computation + let downloading = metadatas.lazy.filter { $0.status == self.global.metadataStatusDownloading }.count + let uploading = metadatas.lazy.filter { $0.status == self.global.metadataStatusUploading }.count + let availableProcess = max(0, NCBrandOptions.shared.numMaximumProcess - (downloading + uploading)) + + // Start Auto Uploads + let metadatasToUpload = Array( + metadatas.lazy.filter { + $0.status == self.global.metadataStatusWaitUpload && + $0.sessionSelector == self.global.selectorUploadAutoUpload && + $0.chunk == 0 + } + .prefix(availableProcess) + ) + + let cameraRoll = NCCameraRoll() + + for metadata in metadatasToUpload { + guard !expired else { return } + + // File exists? skip it + let existsResult = await NCNetworking.shared.fileExists(serverUrlFileName: metadata.serverUrlFileName, account: metadata.account) + if existsResult == .success { + // File exists → delete from local metadata and skip + await NCManageDatabase.shared.deleteMetadataAsync(id: metadata.ocId) + continue + } else if existsResult.errorCode == 404 { + // 404 Not Found → directory does not exist + // Proceed + } else { + // Any other error (423 locked, 401 auth, 403 forbidden, 5xx, etc.) + continue + } + + // Expand seed into concrete metadatas (e.g., Live Photo pair) + let extracted = await cameraRoll.extractCameraRoll(from: metadata) + guard !expired else { return } + + for metadata in extracted { + // Sequential await keeps ordering and simplifies backpressure + let err = await NCNetworking.shared.uploadFileInBackground(metadata: metadata.detachedCopy()) + if err == .success { + nkLog(tag: self.global.logTagBgSync, message: "In queued upload \(metadata.fileName) -> \(metadata.serverUrl)") + } else { + nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Upload failed \(metadata.fileName) -> \(metadata.serverUrl) [\(err.errorDescription)]") + } + guard !expired else { return } + } + + let counter = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND (session == %@ || session == %@) AND status != %d", + account, + NCNetworking.shared.sessionDownloadBackground, + NCNetworking.shared.sessionUploadBackground, + NCGlobal.shared.metadataStatusNormal))?.count ?? 0 + UIApplication.shared.applicationIconBadgeNumber = counter + + NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle") + completion() + } + } + // MARK: - Background Networking Session func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { @@ -230,6 +432,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } + func subscribingPushNotification(account: String, urlBase: String, user: String) { +#if !targetEnvironment(simulator) + NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: UIApplication.shared.firstWindow?.rootViewController) { error in + if error == .success { + NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: account, urlBase: urlBase, user: user, pushKitToken: self.pushKitToken) + } + } +#endif + } + func nextcloudPushNotificationAction(data: [String: AnyObject]) { let account = data["account"] as? String ?? "unavailable" let app = data["app"] as? String @@ -277,6 +489,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func openLogin(selector: Int, window: UIWindow? = nil) { UIApplication.shared.allSceneSessionDestructionExceptFirst() +// func showLoginViewController(_ viewController: UIViewController?) { +// guard let viewController else { return } +// let navigationController = NCLoginNavigationController(rootViewController: viewController) +// +// navigationController.modalPresentationStyle = .fullScreen +// navigationController.navigationBar.barStyle = .black +// navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText +// navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer +// navigationController.navigationBar.isTranslucent = false +// +// if let controller = UIApplication.shared.firstWindow?.rootViewController { +// if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) { +// presentedVC.dismiss(animated: false) { +// controller.present(navigationController, animated: true) +// } +// } else { +// controller.present(navigationController, animated: true) +// } +// } else { +// window?.rootViewController = navigationController +// window?.makeKeyAndVisible() +// } +// } + // Nextcloud standard login if selector == NCGlobal.shared.introSignup { if activeLogin?.view.window == nil { @@ -420,8 +656,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - func trustCertificateError(host: String) { - guard let activeTblAccount = NCManageDatabase.shared.getActiveTableAccount(), - let currentHost = URL(string: activeTblAccount.urlBase)?.host, + guard let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(), + let currentHost = URL(string: activeTableAccount.urlBase)?.host, let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host, host != pushNotificationServerProxyHost, host == currentHost @@ -452,7 +688,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true) } - + // MARK: - Account @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) { @@ -488,7 +724,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD utilityFileSystem.removeDocumentsDirectory() utilityFileSystem.removeTemporaryDirectory() - NCPreferences().removeAll() + NCKeychain().removeAll() + NCNetworking.shared.removeAllKeyUserDefaultsData(account: nil) exit(0) } @@ -510,11 +747,8 @@ extension AppDelegate: NCViewCertificateDetailsDelegate { extension AppDelegate: NCCreateFormUploadConflictDelegate { func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { - if let metadatas { - Task { - await NCManageDatabase.shared.addMetadatasAsync(metadatas) - } - } + guard let metadatas = metadatas, !metadatas.isEmpty else { return } + NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas) } } From ec3269a786b294ab5be7dc57ac7efeb77a7c9c1b Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Mon, 15 Dec 2025 13:29:01 +0530 Subject: [PATCH 4/5] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 88 ++++---- iOSClient/AppDelegate.swift | 284 ++++++++++++++---------- iOSClient/AppUtility.swift | 2 + 3 files changed, 212 insertions(+), 162 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index 226e6f3318..f19dc31069 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -20,9 +20,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! private let titles = [NSLocalizedString("", comment: ""), NSLocalizedString("", comment: ""), NSLocalizedString("", comment: "")] - private var images:[UIImage?] = [] - private var timerAutoScroll: Timer? - + private var images: [UIImage?] = [] + private var timer: Timer? private var textColor: UIColor = .white private var textColorOpponent: UIColor = .black private var activeLoginProvider: NCLoginProvider? @@ -34,9 +33,9 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol override func viewDidLoad() { super.viewDidLoad() - + let isEightPlusDevice = UIScreen.main.bounds.height == 736 - images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) + images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) let isTooLight = NCBrandColor.shared.customer.isTooLight() let isTooDark = NCBrandColor.shared.customer.isTooDark() @@ -61,7 +60,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol self.navigationController?.navigationBar.tintColor = textColor if !NCManageDatabase.shared.getAllTableAccount().isEmpty { - let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(self.actionCancel)) + let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(actionCancel(_:))) navigationItemCancel.tintColor = textColor navigationItem.leftBarButtonItem = navigationItemCancel } @@ -74,13 +73,10 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol buttonLogin.backgroundColor = textColor buttonLogin.setTitle(NSLocalizedString("_log_in_", comment: ""), for: .normal) - buttonSignUp.layer.cornerRadius = 20 - buttonSignUp.layer.borderColor = textColor.cgColor - buttonSignUp.layer.borderWidth = 1.0 + buttonSignUp.layer.cornerRadius = 8 buttonSignUp.setTitleColor(textColor, for: .normal) - buttonSignUp.backgroundColor = NCBrandColor.shared.customer + buttonSignUp.backgroundColor = textColor.withAlphaComponent(0.2) buttonSignUp.titleLabel?.adjustsFontSizeToFitWidth = true - buttonSignUp.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) buttonSignUp.setTitle(NSLocalizedString("_sign_up_", comment: ""), for: .normal) buttonHost.layer.cornerRadius = 20 @@ -95,8 +91,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol view.backgroundColor = NCBrandColor.shared.customer - timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true) - NotificationCenter.default.addObserver(self, selector: #selector(resetPageController(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) + self.timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(self.autoScroll(_:))), userInfo: nil, repeats: true) } override var preferredStatusBarStyle: UIStatusBarStyle { @@ -109,12 +104,12 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if (UIDevice.current.userInterfaceIdiom != .pad){ + if UIDevice.current.userInterfaceIdiom != .pad{ AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) } navigationController?.setNavigationBarHidden(true, animated: animated) } - + override func viewDidLayoutSubviews() { if UIScreen.main.bounds.width < 350 || UIScreen.main.bounds.height > 800 { contstraintBottomLoginButton.constant = 15 @@ -123,25 +118,25 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - timerAutoScroll?.invalidate() + + timer?.invalidate() + timer = nil AppUtility.lockOrientation(UIInterfaceOrientationMask.all) navigationController?.setNavigationBarHidden(false, animated: animated) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - let isEightPlusDevice = UIScreen.main.bounds.height == 736 - images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait) - pageControl.currentPage = 0 - introCollectionView.collectionViewLayout.invalidateLayout() - self.introCollectionView.reloadData() - } - - @objc func resetPageController(_ notification: NSNotification){ - pageControl.currentPage = 0 - introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: true) + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate(alongsideTransition: nil) { _ in + let isEightPlusDevice = UIScreen.main.bounds.height == 736 + self.images = UIDevice.current.orientation.isLandscape ? self.imagesLandscape : (isEightPlusDevice ? self.imagesEightPortrait : self.imagesPortrait) + self.pageControl?.currentPage = 0 + self.introCollectionView?.collectionViewLayout.invalidateLayout() + } } - @objc func autoScroll() { + @objc func autoScroll(_ sender: Any?) { if pageControl.currentPage + 1 >= titles.count { pageControl.currentPage = 0 } else { @@ -174,31 +169,27 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true) - let page = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) - if pageControl.currentPage == (images.count - 1), pageControl.currentPage <= page { - pageControl.currentPage = 0 - introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: false) - } else { - pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) - } + timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(autoScroll(_:))), userInfo: nil, repeats: true) + pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width) } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - timerAutoScroll?.invalidate() - timerAutoScroll = nil + timer?.invalidate() + timer = nil } // MARK: - Action - @objc func actionCancel() { + @objc func actionCancel(_ sender: Any?) { dismiss(animated: true) { } } @IBAction func login(_ sender: Any) { if NCBrandOptions.shared.use_AppConfig { - let loginViewPage = UIStoryboard(name: "NCLogin", bundle: Bundle.main).instantiateViewController(identifier: "NCLogin") - navigationController?.pushViewController(loginViewPage, animated: true) + if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin { + viewController.controller = self.controller + self.navigationController?.pushViewController(viewController, animated: true) + } } else { if NextcloudKit.shared.isNetworkReachable() { appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false) @@ -207,12 +198,6 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } } } - - func showNoInternetAlert(){ - let alertController = UIAlertController(title: NSLocalizedString("_no_internet_alert_title_", comment: ""), message: NSLocalizedString("_no_internet_alert_message_", comment: ""), preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { action in })) - self.present(alertController, animated: true) - } @IBAction func signupWithProvider(_ sender: Any) { let loginProvider = NCLoginProvider() @@ -221,12 +206,23 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol loginProvider.presentingViewController = self loginProvider.startAuthentication() self.activeLoginProvider = loginProvider +// if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider { +// viewController.controller = self.controller +// viewController.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders +// self.navigationController?.pushViewController(viewController, animated: true) +// } } @IBAction func host(_ sender: Any) { guard let url = URL(string: NCBrandOptions.shared.linkLoginHost) else { return } UIApplication.shared.open(url) } + + func showNoInternetAlert() { + let alertController = UIAlertController(title: NSLocalizedString("_no_internet_alert_title_", comment: ""), message: NSLocalizedString("_no_internet_alert_message_", comment: ""), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { action in })) + self.present(alertController, animated: true) + } } extension UINavigationController { diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 304429582e..44f8409978 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -12,7 +12,7 @@ import WidgetKit import Queuer import EasyTipView import SwiftUI -import MoEngageInApps +import RealmSwift class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var backgroundSessionCompletionHandler: (() -> Void)? @@ -25,7 +25,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } var notificationSettings: UNNotificationSettings? - var pushKitToken: String? var loginFlowV2Token = "" var loginFlowV2Endpoint = "" @@ -50,13 +49,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var timerErrorNetworking: Timer? var tipView: EasyTipView? + var pushSubscriptionTask: Task? + var window: UIWindow? + var sceneIdentifier: String = "" + var activeViewController: UIViewController? + var account: String = "" + var urlBase: String = "" + var user: String = "" + var userId: String = "" + var password: String = "" + var timerErrorNetworking: Timer? + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if isUiTestingEnabled { - NCAccount().deleteAllAccounts() + Task { + await NCAccount().deleteAllAccounts() + } } - UINavigationBar.appearance().tintColor = NCBrandColor.shared.customer - UIToolbar.appearance().tintColor = NCBrandColor.shared.customer - let utilityFileSystem = NCUtilityFileSystem() let utility = NCUtility() let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp()) @@ -73,8 +82,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD utilityFileSystem.emptyTemporaryDirectory() utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") + UINavigationBar.appearance().tintColor = NCBrandColor.shared.brand + UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = NCBrandColor.shared.brand + + let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionBuild()) + + NCAppVersionManager.shared.checkAndUpdateInstallState() + NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0) + + UserDefaults.standard.register(defaults: ["UserAgent": userAgent]) + + #if !DEBUG + if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { + FirebaseApp.configure() + } + #endif + NCBrandColor.shared.createUserColors() - NCImageCache.shared.createImagesCache() // Setup Networking // @@ -82,17 +106,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD delegate: NCNetworking.shared) NCNetworking.shared.setupTransferDelegate() - if NCBrandOptions.shared.disable_log { - utilityFileSystem.removeFile(atPath: NextcloudKit.shared.nkCommonInstance.filenamePathLog) - utilityFileSystem.removeFile(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NextcloudKit.shared.nkCommonInstance.filenameLog) - } else { - NextcloudKit.shared.setupLog(pathLog: utilityFileSystem.directoryGroup, - levelLog: NCKeychain().logLevel, - copyLogToDocumentDirectory: true) - NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(NCKeychain().logLevel) " + versionNextcloudiOS) - } + NextcloudKit.configureLogger(logLevel: (NCBrandOptions.shared.disable_log ? .disabled : NCPreferences().log)) + + #if DEBUG +// For the tags look NCGlobal LOG TAG + +// var black: [String] = [] +// black.append("NETWORKING TASKS") +// NextcloudKit.configureLoggerBlacklist(blacklist: black) + +// var white: [String] = [] +// white.append("SYNC METADATA") +// NextcloudKit.configureLoggerWhitelist(whitelist: white) + #endif + + nkLog(start: "Start session with level \(NCPreferences().log) " + versionNextcloudiOS) - /// Push Notification & display notification + // Push Notification & display notification UNUserNotificationCenter.current().getNotificationSettings { settings in self.notificationSettings = settings } @@ -106,6 +136,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD review.showStoreReview() #endif + // BACKGROUND TASK + // BGTaskScheduler.shared.register(forTaskWithIdentifier: global.refreshTask, using: backgroundQueue) { task in guard let appRefreshTask = task as? BGAppRefreshTask else { task.setTaskCompleted(success: false) @@ -113,44 +145,31 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } self.handleAppRefresh(appRefreshTask) } - BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in - self.handleProcessingTask(task) + scheduleAppRefresh() + + BGTaskScheduler.shared.register(forTaskWithIdentifier: global.processingTask, using: backgroundQueue) { task in + guard let processingTask = task as? BGProcessingTask else { + task.setTaskCompleted(success: false) + return + } + self.handleProcessingTask(processingTask) } + scheduleAppProcessing() if NCBrandOptions.shared.enforce_passcode_lock { - NCKeychain().requestPasscodeAtStart = true + NCPreferences().requestPasscodeAtStart = true } - - /// Activation singleton - _ = NCNetworking.shared - _ = NCActionCenter.shared - _ = NCNetworkingProcess.shared - _ = NCTransferProgress.shared - _ = NCActionCenter.shared - - NCTransferProgress.shared.setup() - NCActionCenter.shared.setup() -// if account.isEmpty { -// if NCBrandOptions.shared.disable_intro { -// openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false) -// } else { -// if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() { -// let navigationController = NCLoginNavigationController(rootViewController: viewController) -// window?.rootViewController = navigationController -// window?.makeKeyAndVisible() +// if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { +// for window in windowScene.windows { +// let imageViews = window.allImageViews() +// // Do something with the imageViews +// for imageView in imageViews { +// print("Found image view: \(imageView)") +// imageView.tintColor = UITraitCollection.current.userInterfaceStyle == .dark ? .white : .black // } // } -// } else { -// NCPasscode.shared.presentPasscode(delegate: self) { -// NCPasscode.shared.enableTouchFaceID() -// } // } - adjust.configAdjust() - adjust.subsessionStart() - TealiumHelper.shared.start() - FirebaseApp.configure() - return true } @@ -188,13 +207,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage. */ func scheduleAppRefresh() { - let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask) + let request = BGAppRefreshTaskRequest(identifier: global.refreshTask) request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds. + do { try BGTaskScheduler.shared.submit(request) } catch { - NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)") + nkLog(tag: self.global.logTagTask, emoji: .error, message: "Refresh task failed to submit request: \(error)") } } @@ -202,19 +222,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week. */ func scheduleAppProcessing() { - let request = BGProcessingTaskRequest(identifier: NCGlobal.shared.processingTask) + let request = BGProcessingTaskRequest(identifier: global.processingTask) request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes. request.requiresNetworkConnectivity = false request.requiresExternalPower = false + do { try BGTaskScheduler.shared.submit(request) } catch { - NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)") + nkLog(tag: self.global.logTagTask, emoji: .error, message: "Processing task failed to submit request: \(error)") } } - func handleAppRefresh(_ task: BGTask) { + func handleAppRefresh(_ task: BGAppRefreshTask) { + nkLog(tag: self.global.logTagTask, emoji: .start, message: "Start refresh task") + guard NCManageDatabase.shared.openRealmBackground() else { + nkLog(tag: self.global.logTagTask, emoji: .error, message: "Failed to open Realm in background") + task.setTaskCompleted(success: false) + return + } + + // Schedule next refresh scheduleAppRefresh() Task { @@ -222,7 +251,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD task.setTaskCompleted(success: true) } - await backgroundSync(task: task) + guard let tblAccount = await NCManageDatabase.shared.getActiveTableAccountAsync() else { + nkLog(tag: self.global.logTagTask, emoji: .info, message: "No active account or background task already running") + return + } + + await backgroundSync(tblAccount: tblAccount, task: task) } } @@ -303,16 +337,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD guard !expired else { return } let err = await NCNetworking.shared.createFolderForAutoUpload( - serverUrlFileName: metadata.serverUrlFileName, - account: metadata.account + serverUrlFileName: meta.serverUrlFileName, + account: meta.account ) // Fail-fast: abort the whole sync on first failure if err != .success { - nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Create folder '\(metadata.serverUrlFileName)' failed: \(err.errorCode) – aborting sync") + nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Create folder '\(meta.serverUrlFileName)' failed: \(err.errorCode) – aborting sync") return } } - + // Capacity computation let downloading = metadatas.lazy.filter { $0.status == self.global.metadataStatusDownloading }.count let uploading = metadatas.lazy.filter { $0.status == self.global.metadataStatusUploading }.count @@ -361,16 +395,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } guard !expired else { return } } - - let counter = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND (session == %@ || session == %@) AND status != %d", - account, - NCNetworking.shared.sessionDownloadBackground, - NCNetworking.shared.sessionUploadBackground, - NCGlobal.shared.metadataStatusNormal))?.count ?? 0 - UIApplication.shared.applicationIconBadgeNumber = counter - - NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle") - completion() } } @@ -432,16 +456,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } - func subscribingPushNotification(account: String, urlBase: String, user: String) { -#if !targetEnvironment(simulator) - NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: UIApplication.shared.firstWindow?.rootViewController) { error in - if error == .success { - NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: account, urlBase: urlBase, user: user, pushKitToken: self.pushKitToken) - } - } -#endif - } - func nextcloudPushNotificationAction(data: [String: AnyObject]) { let account = data["account"] as? String ?? "unavailable" let app = data["app"] as? String @@ -484,41 +498,83 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } + // MARK: - Trust Certificate Error + + func trustCertificateError(host: String) { + guard let activeTblAccount = NCManageDatabase.shared.getActiveTableAccount(), + let currentHost = URL(string: activeTblAccount.urlBase)?.host, + let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host, + host != pushNotificationServerProxyHost, + host == currentHost + else { return } + let certificateHostSavedPath = NCUtilityFileSystem().directoryCertificates + "/" + host + ".der" + var title = NSLocalizedString("_ssl_certificate_changed_", comment: "") + + if !FileManager.default.fileExists(atPath: certificateHostSavedPath) { + title = NSLocalizedString("_connect_server_anyway_", comment: "") + } + + let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in + NCNetworking.shared.writeCertificate(host: host) + })) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in })) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in + if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController, + let viewController = navigationController.topViewController as? NCViewCertificateDetails { + viewController.delegate = self + viewController.host = host + UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true) + } + })) + + UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true) + } + + // MARK: - Reset Application + + func resetApplication() { + let utilityFileSystem = NCUtilityFileSystem() + + NCNetworking.shared.cancelAllTask() + + URLCache.shared.removeAllCachedResponses() + + utilityFileSystem.removeGroupDirectoryProviderStorage() + utilityFileSystem.removeGroupApplicationSupport() + utilityFileSystem.removeDocumentsDirectory() + utilityFileSystem.removeTemporaryDirectory() + + NCPreferences().removeAll() + + // Reset App Icon badge / File Icon badge + if #available(iOS 17.0, *) { + UNUserNotificationCenter.current().setBadgeCount(0) + } + exit(0) + } + + // MARK: - Universal Links + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + let applicationHandle = NCApplicationHandle() + return applicationHandle.applicationOpenUserActivity(userActivity) + } + // MARK: - Login func openLogin(selector: Int, window: UIWindow? = nil) { UIApplication.shared.allSceneSessionDestructionExceptFirst() -// func showLoginViewController(_ viewController: UIViewController?) { -// guard let viewController else { return } -// let navigationController = NCLoginNavigationController(rootViewController: viewController) -// -// navigationController.modalPresentationStyle = .fullScreen -// navigationController.navigationBar.barStyle = .black -// navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText -// navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer -// navigationController.navigationBar.isTranslucent = false -// -// if let controller = UIApplication.shared.firstWindow?.rootViewController { -// if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) { -// presentedVC.dismiss(animated: false) { -// controller.present(navigationController, animated: true) -// } -// } else { -// controller.present(navigationController, animated: true) -// } -// } else { -// window?.rootViewController = navigationController -// window?.makeKeyAndVisible() -// } -// } - // Nextcloud standard login if selector == NCGlobal.shared.introSignup { if activeLogin?.view.window == nil { if selector == NCGlobal.shared.introSignup { let web = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider - web?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders + web?.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders showLoginViewController(web) } else { activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin @@ -538,7 +594,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } - @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) { + func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) { // openLogin(selector: NCGlobal.shared.introLogin) // [WEBPersonalized] [AppConfig] if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig { @@ -593,7 +649,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func showLoginViewController(_ viewController: UIViewController?) { guard let viewController else { return } - let navigationController = NCLoginNavigationController(rootViewController: viewController) + let navigationController = UINavigationController(rootViewController: viewController) navigationController.modalPresentationStyle = .fullScreen navigationController.navigationBar.barStyle = .black @@ -602,7 +658,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD navigationController.navigationBar.isTranslucent = false if let controller = UIApplication.shared.firstWindow?.rootViewController { - if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) { + if let presentedVC = controller.presentedViewController, !(presentedVC is UINavigationController) { presentedVC.dismiss(animated: false) { controller.present(navigationController, animated: true) } @@ -619,7 +675,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if contextViewController == nil { if let viewController = viewController { - let navigationController = NCLoginNavigationController(rootViewController: viewController) + let navigationController = UINavigationController(rootViewController: viewController) navigationController.navigationBar.barStyle = .black navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer @@ -633,7 +689,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } else { if let viewController = viewController, let contextViewController = contextViewController { - let navigationController = NCLoginNavigationController(rootViewController: viewController) + let navigationController = UINavigationController(rootViewController: viewController) navigationController.modalPresentationStyle = .fullScreen navigationController.navigationBar.barStyle = .black navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText @@ -747,14 +803,10 @@ extension AppDelegate: NCViewCertificateDetailsDelegate { extension AppDelegate: NCCreateFormUploadConflictDelegate { func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { - guard let metadatas = metadatas, !metadatas.isEmpty else { return } - NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas) - } -} - -//MARK: NMC Customisation -extension AppDelegate { - func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { - return self.orientationLock + if let metadatas { + Task { + await NCManageDatabase.shared.addMetadatasAsync(metadatas) + } + } } } diff --git a/iOSClient/AppUtility.swift b/iOSClient/AppUtility.swift index bb7c625e53..6d940416ca 100644 --- a/iOSClient/AppUtility.swift +++ b/iOSClient/AppUtility.swift @@ -7,6 +7,8 @@ // import Foundation +import UIKit + struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { From cbf93311fee82538637b50677b0a694818117ce9 Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Tue, 16 Jun 2026 18:22:27 +0530 Subject: [PATCH 5/5] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 23 ++- iOSClient/AppDelegate.swift | 184 +++++------------------- 2 files changed, 50 insertions(+), 157 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index f19dc31069..73a58fb7b5 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -19,12 +19,13 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol var controller: NCMainTabBarController? private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! +// private let titles = [NSLocalizedString("_intro_1_title_", comment: ""), NSLocalizedString("_intro_2_title_", comment: ""), NSLocalizedString("_intro_3_title_", comment: ""), NSLocalizedString("_intro_4_title_", comment: "")] +// private var images = [UIImage(named: "intro1"), UIImage(named: "intro2"), UIImage(named: "intro3"), UIImage(named: "intro4")] private let titles = [NSLocalizedString("", comment: ""), NSLocalizedString("", comment: ""), NSLocalizedString("", comment: "")] private var images: [UIImage?] = [] private var timer: Timer? private var textColor: UIColor = .white private var textColorOpponent: UIColor = .black - private var activeLoginProvider: NCLoginProvider? private let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")] private let imagesPortrait = [UIImage(named: "introSlide1"), UIImage(named: "introSlide2"), UIImage(named: "introSlide3")] private let imagesEightPortrait = [UIImage(named: "introSlideEight1"), UIImage(named: "introSlideEight2"), UIImage(named: "introSlideEight3")] @@ -185,6 +186,10 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } @IBAction func login(_ sender: Any) { +// if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin { +// viewController.controller = self.controller +// self.navigationController?.pushViewController(viewController, animated: true) +// } if NCBrandOptions.shared.use_AppConfig { if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin { viewController.controller = self.controller @@ -200,17 +205,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } @IBAction func signupWithProvider(_ sender: Any) { - let loginProvider = NCLoginProvider() - loginProvider.controller = self.controller - loginProvider.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders - loginProvider.presentingViewController = self - loginProvider.startAuthentication() - self.activeLoginProvider = loginProvider -// if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider { -// viewController.controller = self.controller -// viewController.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders -// self.navigationController?.pushViewController(viewController, animated: true) -// } + if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider { + viewController.controller = self.controller + viewController.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders + self.navigationController?.pushViewController(viewController, animated: true) + } } @IBAction func host(_ sender: Any) { diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 44f8409978..f10ce6b567 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -13,6 +13,7 @@ import Queuer import EasyTipView import SwiftUI import RealmSwift +import MoEngageInApps class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var backgroundSessionCompletionHandler: (() -> Void)? @@ -38,18 +39,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD let database = NCManageDatabase.shared - var window: UIWindow? - @objc var sceneIdentifier: String = "" - @objc var activeViewController: UIViewController? - @objc var account: String = "" - @objc var urlBase: String = "" - @objc var user: String = "" - @objc var userId: String = "" - @objc var password: String = "" - var timerErrorNetworking: Timer? - var tipView: EasyTipView? - - var pushSubscriptionTask: Task? var window: UIWindow? var sceneIdentifier: String = "" var activeViewController: UIViewController? @@ -59,7 +48,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var userId: String = "" var password: String = "" var timerErrorNetworking: Timer? - + var tipView: EasyTipView? + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if isUiTestingEnabled { Task { @@ -68,16 +58,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } let utilityFileSystem = NCUtilityFileSystem() let utility = NCUtility() - let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp()) - - NCAppVersionManager.shared.checkAndUpdateInstallState() - NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0) - - UserDefaults.standard.register(defaults: ["UserAgent": userAgent]) - if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { - FirebaseApp.configure() - } utilityFileSystem.createDirectoryStandard() utilityFileSystem.emptyTemporaryDirectory() utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") @@ -92,13 +73,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UserDefaults.standard.register(defaults: ["UserAgent": userAgent]) - #if !DEBUG - if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { - FirebaseApp.configure() - } - #endif +// #if !DEBUG +// if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { +// FirebaseApp.configure() +// } +// #endif NCBrandColor.shared.createUserColors() + NCImageCache.shared.createImagesCache() // Setup Networking // @@ -159,18 +141,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if NCBrandOptions.shared.enforce_passcode_lock { NCPreferences().requestPasscodeAtStart = true } - -// if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { -// for window in windowScene.windows { -// let imageViews = window.allImageViews() -// // Do something with the imageViews -// for imageView in imageViews { -// print("Found image view: \(imageView)") -// imageView.tintColor = UITraitCollection.current.userInterfaceStyle == .dark ? .white : .black -// } -// } -// } + adjust.configAdjust() + adjust.subsessionStart() + TealiumHelper.shared.start() + FirebaseApp.configure() + + // Initialize MoEngage early in app lifecycle + MoEngageAnalytics.setupIfNeeded() + return true } @@ -251,12 +230,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD task.setTaskCompleted(success: true) } - guard let tblAccount = await NCManageDatabase.shared.getActiveTableAccountAsync() else { - nkLog(tag: self.global.logTagTask, emoji: .info, message: "No active account or background task already running") - return - } - - await backgroundSync(tblAccount: tblAccount, task: task) + await backgroundSync(task: task) } } @@ -337,12 +311,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD guard !expired else { return } let err = await NCNetworking.shared.createFolderForAutoUpload( - serverUrlFileName: meta.serverUrlFileName, - account: meta.account + serverUrlFileName: metadata.serverUrlFileName, + account: metadata.account ) // Fail-fast: abort the whole sync on first failure if err != .success { - nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Create folder '\(meta.serverUrlFileName)' failed: \(err.errorCode) – aborting sync") + nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Create folder '\(metadata.serverUrlFileName)' failed: \(err.errorCode) – aborting sync") return } } @@ -425,11 +399,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - guard !isXcodeRunningForPreviews, - application.applicationState != .background else { - return - } - if let deviceToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) { NCPreferences().deviceTokenPushNotification = deviceToken pushSubscriptionTask = Task.detached { @@ -440,7 +409,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return } - try? await Task.sleep(for: .seconds(1)) + try? await Task.sleep(nanoseconds: 1_000_000_000) let tblAccounts = await NCManageDatabase.shared.getAllTableAccountAsync() for tblAccount in tblAccounts { @@ -457,6 +426,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func nextcloudPushNotificationAction(data: [String: AnyObject]) { + guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data) + else { + return + } let account = data["account"] as? String ?? "unavailable" let app = data["app"] as? String @@ -464,7 +437,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if app == NCGlobal.shared.termsOfServiceName { Task { await NCNetworking.shared.transferDispatcher.notifyAllDelegatesAsync { delegate in - try? await Task.sleep(for: .seconds(0.5)) + try? await Task.sleep(nanoseconds: 500_000_000) delegate.transferReloadDataSource(serverUrl: nil, requestData: true, status: nil) } } @@ -487,18 +460,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD openNotification(controller: controller) } } else { - let message = String( - format: NSLocalizedString("account_does_not_exist", comment: ""), - account - ) - + let message = NSLocalizedString("_the_account_", comment: "") + " " + account + " " + 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 })) UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true, completion: { }) } } - // MARK: - Trust Certificate Error + // MARK: - func trustCertificateError(host: String) { guard let activeTblAccount = NCManageDatabase.shared.getActiveTableAccount(), @@ -527,11 +496,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD let viewController = navigationController.topViewController as? NCViewCertificateDetails { viewController.delegate = self viewController.host = host - UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true) + UIApplication.shared.mainAppWindow?.rootViewController?.present(navigationController, animated: true) } })) - UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true) + UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true) } // MARK: - Reset Application @@ -708,89 +677,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD guard !account.isEmpty, NCKeychain().getPassword(account: account).isEmpty else { return } openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true) } - - // MARK: - - - func trustCertificateError(host: String) { - guard let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(), - let currentHost = URL(string: activeTableAccount.urlBase)?.host, - let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host, - host != pushNotificationServerProxyHost, - host == currentHost - else { return } - let certificateHostSavedPath = NCUtilityFileSystem().directoryCertificates + "/" + host + ".der" - var title = NSLocalizedString("_ssl_certificate_changed_", comment: "") - - if !FileManager.default.fileExists(atPath: certificateHostSavedPath) { - title = NSLocalizedString("_connect_server_anyway_", comment: "") - } - - let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert) - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in - NCNetworking.shared.writeCertificate(host: host) - })) - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in })) - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in - if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController, - let viewController = navigationController.topViewController as? NCViewCertificateDetails { - viewController.delegate = self - viewController.host = host - UIApplication.shared.mainAppWindow?.rootViewController?.present(navigationController, animated: true) - } - })) - - UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true) - } - - // MARK: - Account - - @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) { -// NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser) - } - - @objc func deleteAccount(_ account: String, wipe: Bool) { - NCAccount().deleteAccount(account, wipe: wipe) - } - - func deleteAllAccounts() { - let accounts = NCManageDatabase.shared.getAccounts() - accounts?.forEach({ account in - deleteAccount(account, wipe: true) - }) - } - - func updateShareAccounts() -> Error? { - return NCAccount().updateAppsShareAccounts() - } - - // MARK: - Reset Application - - @objc func resetApplication() { - let utilityFileSystem = NCUtilityFileSystem() - - NCNetworking.shared.cancelAllTask() - - URLCache.shared.removeAllCachedResponses() - - utilityFileSystem.removeGroupDirectoryProviderStorage() - utilityFileSystem.removeGroupApplicationSupport() - utilityFileSystem.removeDocumentsDirectory() - utilityFileSystem.removeTemporaryDirectory() - - NCKeychain().removeAll() - NCNetworking.shared.removeAllKeyUserDefaultsData(account: nil) - - exit(0) - } - - // MARK: - Universal Links - - func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - return false - } } // MARK: - Extension @@ -810,3 +696,11 @@ extension AppDelegate: NCCreateFormUploadConflictDelegate { } } } + +//MARK: NMC Customisation +extension AppDelegate { + func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { + return self.orientationLock + } +} +