From 51f9ad8a85780ef63cc053abe538d9a062de7f41 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/4] 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 c626a4c557..e0ceaf6d3c 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,22 +12,30 @@ 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 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() @@ -82,7 +91,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 { @@ -93,20 +103,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?) { @@ -133,6 +161,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 } @@ -141,8 +170,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) { @@ -157,11 +192,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) { if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? 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 7fcf867ea8..1478b12348 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -17,6 +17,8 @@ import RealmSwift @UIApplicationMain 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") } @@ -517,3 +519,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 8927e6d954bc67cca7af019cb7d16b0f8a9ce3b0 Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Wed, 9 Apr 2025 12:11:56 +0530 Subject: [PATCH 2/4] 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 e0ceaf6d3c..828bb1bf8d 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -197,7 +197,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 8dd4c5545f..0fcc867beb 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -85,6 +85,11 @@ AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; }; AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; }; AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.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 */; }; F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; }; @@ -1221,6 +1226,9 @@ AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; }; AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; }; AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.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 = ""; }; @@ -2024,6 +2032,8 @@ isa = PBXGroup; children = ( AA52EB452D42AC5A0089C348 /* Placeholder.swift */, + B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */, + AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */, ); path = NextcloudUnitTests; sourceTree = ""; @@ -3226,6 +3236,7 @@ isa = PBXGroup; children = ( AA517BB42D66149900F8D37C /* .tx */, + B54315332DA64D6500981E7E /* AppUtility.swift */, F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */, F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */, F7CF067A2E0FF38F0063AD04 /* NCAppStateManager.swift */, @@ -4590,6 +4601,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 */, F3E173B02C9AF637006D177A /* ScreenAwakeManager.swift in Sources */, diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 1478b12348..7906abf2af 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -17,8 +17,11 @@ import RealmSwift @UIApplicationMain 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") } @@ -34,6 +37,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 { @@ -61,6 +74,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD #endif NCBrandColor.shared.createUserColors() + NCImageCache.shared.createImagesCache() // Setup Networking // @@ -122,6 +136,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 } @@ -439,6 +464,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) { @@ -475,9 +645,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 ca1021ac205eaa8633198d5e69a0a73437f3670a Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Tue, 30 Sep 2025 21:27:11 +0530 Subject: [PATCH 3/4] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 28 ++-- Brand/LaunchScreen.storyboard | 2 +- iOSClient/AppDelegate.swift | 210 +++++++++++++----------- 3 files changed, 133 insertions(+), 107 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index 828bb1bf8d..d4ee917c6f 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -60,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: .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 } @@ -73,10 +73,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 @@ -137,7 +140,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 { @@ -181,13 +184,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) { } } @@ -197,7 +200,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() @@ -212,11 +214,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } @IBAction func signupWithProvider(_ sender: Any) { - 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) - } + appDelegate.openLogin(selector: NCGlobal.shared.introSignup) + } + + @IBAction func signup(_ sender: Any) { + appDelegate.openLogin(selector: NCGlobal.shared.introSignup) } @IBAction func host(_ sender: Any) { 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 7906abf2af..f848b848c0 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 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { @@ -26,6 +26,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } var notificationSettings: UNNotificationSettings? + var pushKitToken: String? var loginFlowV2Token = "" var loginFlowV2Endpoint = "" @@ -37,6 +38,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? @@ -46,32 +49,30 @@ 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) UserDefaults.standard.register(defaults: ["UserAgent": userAgent]) - - #if !DEBUG - if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { + if !NCKeychain().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { FirebaseApp.configure() } - #endif + + utilityFileSystem.createDirectoryStandard() + utilityFileSystem.emptyTemporaryDirectory() + utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") NCBrandColor.shared.createUserColors() NCImageCache.shared.createImagesCache() @@ -82,23 +83,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 } @@ -112,41 +107,49 @@ 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) - return - } - self.handleAppRefresh(appRefreshTask) + /// Background task register + BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in + self.handleAppRefresh(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) + 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 } @@ -160,7 +163,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD notificationCenter.add(req) } - nkLog(debug: "bye bye") + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] bye bye") } // MARK: - UISceneSession Lifecycle @@ -183,14 +186,13 @@ 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: global.refreshTask) + let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask) request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds. - do { try BGTaskScheduler.shared.submit(request) } catch { - nkLog(tag: self.global.logTagTask, emoji: .error, message: "Refresh task failed to submit request: \(error)") + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)") } } @@ -198,28 +200,19 @@ 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: global.processingTask) + 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 { - nkLog(tag: self.global.logTagTask, emoji: .error, message: "Processing task failed to submit request: \(error)") + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)") } } - 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 + func handleAppRefresh(_ task: BGTask) { scheduleAppRefresh() Task { @@ -317,7 +310,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD 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 @@ -366,6 +359,16 @@ 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() } } @@ -396,22 +399,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - if let deviceToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) { - NCPreferences().deviceTokenPushNotification = deviceToken - pushSubscriptionTask = Task.detached { - // Wait bounded time for maintenance to be OFF - let canProceed = await NCAppStateManager.shared.waitForMaintenanceOffAsync() - guard canProceed else { - nkLog(error: "[PUSH] Skipping subscription: maintenance mode still ON after timeout") - return - } - - try? await Task.sleep(nanoseconds: 1_000_000_000) - - let tblAccounts = await NCManageDatabase.shared.getAllTableAccountAsync() - for tblAccount in tblAccounts { - await NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase) - } + if let pushKitToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) { + self.pushKitToken = pushKitToken + // https://github.com/nextcloud/talk-ios/issues/691 + for tblAccount in NCManageDatabase.shared.getAllTableAccount() { + subscribingPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase, user: tblAccount.user) } } } @@ -422,6 +414,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]) { guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data) else { @@ -469,6 +471,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 { @@ -612,8 +638,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 @@ -644,7 +670,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UIApplication.shared.mainAppWindow?.rootViewController?.present(alertController, animated: true) } - + // MARK: - Account @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) { @@ -680,7 +706,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD utilityFileSystem.removeDocumentsDirectory() utilityFileSystem.removeTemporaryDirectory() - NCPreferences().removeAll() + NCKeychain().removeAll() + NCNetworking.shared.removeAllKeyUserDefaultsData(account: nil) exit(0) } @@ -703,11 +730,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 760a5f141f5f793eb18d97bfd800b872b0cf409e Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Mon, 15 Dec 2025 13:29:01 +0530 Subject: [PATCH 4/4] NMC 1971 - AppDelegate and Intro screen UI Customisation --- Brand/Intro/NCIntroViewController.swift | 93 ++++--- iOSClient/AppDelegate.swift | 319 ++++++++++++++---------- iOSClient/AppUtility.swift | 2 + 3 files changed, 232 insertions(+), 182 deletions(-) diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index d4ee917c6f..5ffb2466af 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 let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")] @@ -33,9 +32,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() @@ -60,7 +59,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 } @@ -73,13 +72,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 @@ -94,8 +90,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 { @@ -108,12 +103,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 @@ -122,25 +117,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 { @@ -173,31 +168,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) @@ -206,25 +197,25 @@ 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) { - appDelegate.openLogin(selector: NCGlobal.shared.introSignup) - } - - @IBAction func signup(_ sender: Any) { - appDelegate.openLogin(selector: NCGlobal.shared.introSignup) + 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 f848b848c0..896d761308 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 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { @@ -26,7 +26,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return ProcessInfo.processInfo.arguments.contains("UI_TESTING") } var notificationSettings: UNNotificationSettings? - var pushKitToken: String? var loginFlowV2Token = "" var loginFlowV2Endpoint = "" @@ -51,31 +50,47 @@ 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()) + + utilityFileSystem.createDirectoryStandard() + 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 !NCKeychain().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { + + #if !DEBUG + if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service { FirebaseApp.configure() } - - utilityFileSystem.createDirectoryStandard() - utilityFileSystem.emptyTemporaryDirectory() - utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto") + #endif NCBrandColor.shared.createUserColors() - NCImageCache.shared.createImagesCache() // Setup Networking // @@ -83,17 +98,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 - /// Push Notification & display notification +// 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 UNUserNotificationCenter.current().getNotificationSettings { settings in self.notificationSettings = settings } @@ -107,48 +128,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD review.showStoreReview() #endif - /// Background task register - BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in - self.handleAppRefresh(task) + // BACKGROUND TASK + // + BGTaskScheduler.shared.register(forTaskWithIdentifier: global.refreshTask, using: backgroundQueue) { task in + guard let appRefreshTask = task as? BGAppRefreshTask else { + task.setTaskCompleted(success: false) + return + } + 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 } @@ -163,7 +176,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD notificationCenter.add(req) } - NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] bye bye") + nkLog(debug: "bye bye") } // MARK: - UISceneSession Lifecycle @@ -186,13 +199,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)") } } @@ -200,19 +214,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 { @@ -220,7 +243,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) } } @@ -301,16 +329,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 @@ -359,16 +387,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() } } @@ -399,11 +417,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - if let pushKitToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) { - self.pushKitToken = pushKitToken - // https://github.com/nextcloud/talk-ios/issues/691 - for tblAccount in NCManageDatabase.shared.getAllTableAccount() { - subscribingPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase, user: tblAccount.user) + if let deviceToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) { + NCPreferences().deviceTokenPushNotification = deviceToken + pushSubscriptionTask = Task.detached { + // Wait bounded time for maintenance to be OFF + let canProceed = await NCAppStateManager.shared.waitForMaintenanceOffAsync() + guard canProceed else { + nkLog(error: "[PUSH] Skipping subscription: maintenance mode still ON after timeout") + return + } + + try? await Task.sleep(nanoseconds: 1_000_000_000) + + let tblAccounts = await NCManageDatabase.shared.getAllTableAccountAsync() + for tblAccount in tblAccounts { + await NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase) + } } } } @@ -414,16 +443,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]) { guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data) else { @@ -466,41 +485,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 @@ -520,7 +581,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 { @@ -575,7 +636,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 @@ -584,7 +645,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) } @@ -601,7 +662,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 @@ -615,7 +676,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 @@ -730,14 +791,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 {