diff --git a/Brand/File_Provider_Extension.entitlements b/Brand/File_Provider_Extension.entitlements
index 4ecc3f0d13..e3818a0874 100755
--- a/Brand/File_Provider_Extension.entitlements
+++ b/Brand/File_Provider_Extension.entitlements
@@ -4,11 +4,11 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)MagentaCLOUD
diff --git a/Brand/File_Provider_Extension.plist b/Brand/File_Provider_Extension.plist
index 7179d95e3a..acf5472307 100755
--- a/Brand/File_Provider_Extension.plist
+++ b/Brand/File_Provider_Extension.plist
@@ -5,7 +5,7 @@
CFBundleDevelopmentRegion
en
CFBundleDisplayName
- Nextcloud
+ MagentaCLOUD
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -19,7 +19,7 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
+ $(CURRENT_PROJECT_VERSION)
NSAppTransportSecurity
NSAllowsArbitraryLoads
@@ -27,13 +27,8 @@
NSExtension
- NSExtensionAttributes
-
- NSFileProviderSupportsMultipleDomains
-
-
NSExtensionFileProviderDocumentGroup
- group.it.twsweb.Crypto-Cloud
+ group.de.telekom.Mediencenter
NSExtensionFileProviderSupportsEnumeration
NSExtensionFileProviderSupportsPickingFolders
diff --git a/Brand/File_Provider_Extension_UI.entitlements b/Brand/File_Provider_Extension_UI.entitlements
index 4ecc3f0d13..3128ba985e 100644
--- a/Brand/File_Provider_Extension_UI.entitlements
+++ b/Brand/File_Provider_Extension_UI.entitlements
@@ -4,11 +4,12 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
+ group.de.magentacloud.next.dev2.client
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)de.magentacloud.next.dev2.client
diff --git a/Brand/File_Provider_Extension_UI.plist b/Brand/File_Provider_Extension_UI.plist
index 5e27de9fde..421f512fd5 100644
--- a/Brand/File_Provider_Extension_UI.plist
+++ b/Brand/File_Provider_Extension_UI.plist
@@ -2,8 +2,24 @@
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
- Nextcloud
+ MagentaCLOUD
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionFileProviderActions
@@ -29,16 +45,12 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
+ 36215
CFBundleInfoDictionaryVersion
6.0
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
NSHumanReadableCopyright
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundlePackageType
- $(PRODUCT_BUNDLE_PACKAGE_TYPE)
diff --git a/Brand/Notification_Service_Extension.entitlements b/Brand/Notification_Service_Extension.entitlements
index 9cf2061f89..4a9ebe9cb6 100644
--- a/Brand/Notification_Service_Extension.entitlements
+++ b/Brand/Notification_Service_Extension.entitlements
@@ -6,13 +6,13 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
com.apple.security.network.client
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)MagentaCLOUD
diff --git a/Brand/Notification_Service_Extension.plist b/Brand/Notification_Service_Extension.plist
index 7cba1da5f6..ebc8f313de 100644
--- a/Brand/Notification_Service_Extension.plist
+++ b/Brand/Notification_Service_Extension.plist
@@ -19,7 +19,7 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
+ $(CURRENT_PROJECT_VERSION)
NSExtension
NSExtensionPointIdentifier
diff --git a/Brand/Share.entitlements b/Brand/Share.entitlements
index 9cf2061f89..4a9ebe9cb6 100755
--- a/Brand/Share.entitlements
+++ b/Brand/Share.entitlements
@@ -6,13 +6,13 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
com.apple.security.network.client
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)MagentaCLOUD
diff --git a/Brand/Share.plist b/Brand/Share.plist
index ec2daea2b1..967731ef66 100755
--- a/Brand/Share.plist
+++ b/Brand/Share.plist
@@ -5,7 +5,7 @@
CFBundleDevelopmentRegion
en
CFBundleDisplayName
- Nextcloud
+ MagentaCLOUD
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -19,18 +19,19 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
+ $(CURRENT_PROJECT_VERSION)
NSAppTransportSecurity
NSAllowsArbitraryLoads
-
+
NSExtension
NSExtensionAttributes
NSExtensionActivationRule
- SUBQUERY (extensionItems, $extensionItem, SUBQUERY ($extensionItem.attachments, $attachment, (ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data" OR ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text")).@count == $extensionItem.attachments.@count).@count > 0
+ SUBQUERY (extensionItems, $extensionItem, SUBQUERY ($extensionItem.attachments,$attachment,(ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data")).@count == $extensionItem.attachments.@count).@count > 0
+
NSExtensionMainStoryboard
MainInterface
@@ -38,4 +39,4 @@
com.apple.share-services
-
\ No newline at end of file
+
diff --git a/Brand/Widget.entitlements b/Brand/Widget.entitlements
index 9cf2061f89..b45faa35aa 100644
--- a/Brand/Widget.entitlements
+++ b/Brand/Widget.entitlements
@@ -6,13 +6,14 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
+ group.de.magentacloud.next.dev2.client
com.apple.security.network.client
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)de.magentacloud.next.dev2.client
diff --git a/Brand/Widget.plist b/Brand/Widget.plist
index 32ce55cfb4..d4e598ee31 100644
--- a/Brand/Widget.plist
+++ b/Brand/Widget.plist
@@ -2,15 +2,15 @@
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
NSExtension
NSExtensionPointIdentifier
com.apple.widgetkit-extension
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
diff --git a/Brand/WidgetDashboardIntentHandler.entitlements b/Brand/WidgetDashboardIntentHandler.entitlements
index 9cf2061f89..b45faa35aa 100644
--- a/Brand/WidgetDashboardIntentHandler.entitlements
+++ b/Brand/WidgetDashboardIntentHandler.entitlements
@@ -6,13 +6,14 @@
com.apple.security.application-groups
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
+ group.de.magentacloud.next.dev2.client
com.apple.security.network.client
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)de.magentacloud.next.dev2.client
diff --git a/Brand/iOSClient.entitlements b/Brand/iOSClient.entitlements
index f4b9c9dcce..418cd0cb52 100755
--- a/Brand/iOSClient.entitlements
+++ b/Brand/iOSClient.entitlements
@@ -8,8 +8,7 @@
com.apple.security.application-groups
- group.com.nextcloud.apps
- group.it.twsweb.Crypto-Cloud
+ group.com.t-systems.pu-ds.magentacloud.qa
com.apple.security.device.audio-input
@@ -23,7 +22,7 @@
keychain-access-groups
- $(AppIdentifierPrefix)it.twsweb.Crypto-Cloud
+ $(AppIdentifierPrefix)MagentaCLOUD
diff --git a/Brand/iOSClient.plist b/Brand/iOSClient.plist
index cff62ea040..fbb6262ef5 100755
--- a/Brand/iOSClient.plist
+++ b/Brand/iOSClient.plist
@@ -12,13 +12,13 @@
CFBundleDevelopmentRegion
en
CFBundleDisplayName
- Nextcloud
+ MagentaCLOUD
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
- 6.0
+ $(MARKETING_VERSION)
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
@@ -43,13 +43,14 @@
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
ITSAppUsesNonExemptEncryption
-
+
ITSEncryptionExportComplianceCode
8e9f9874-938e-460b-a9be-f82cb3393971
LSApplicationQueriesSchemes
nextcloudtalk
nextcloudnotes
+ itms-apps
LSMinimumSystemVersion
12.3
@@ -83,6 +84,8 @@
AccountIntent
DashboardIntent
+ NSUserTrackingUsageDescription
+ App would like to access IDFA for tracking purpose
PHPhotoLibraryPreventAutomaticLimitedAccessAlert
UIAppFonts
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index b24d42ac75..5d9af17cf3 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -42,7 +42,6 @@
AAA7BC2E2D3E39F1008F1A22 /* CapabilitiesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7BC2D2D3E39EC008F1A22 /* CapabilitiesResponse.swift */; };
AAA7BC302D3E3B88008F1A22 /* CapabilityResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7BC2F2D3E3B83008F1A22 /* CapabilityResponse.swift */; };
AABD0C8A2D5F67A400F009E6 /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C892D5F67A200F009E6 /* XCUIElement.swift */; };
- AABD0C9B2D5F73FC00F009E6 /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C9A2D5F73FA00F009E6 /* Placeholder.swift */; };
AAE330042D2ED20200B04903 /* NCShareNavigationTitleSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE330032D2ED1FF00B04903 /* NCShareNavigationTitleSetting.swift */; };
AAFC0D042F9AA10000F0A001 /* NCFocusedAutoUploadIntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFC0D012F9AA10000F0A001 /* NCFocusedAutoUploadIntroView.swift */; };
AAFC0D052F9AA10000F0A001 /* NCFocusedAutoUploadProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFC0D022F9AA10000F0A001 /* NCFocusedAutoUploadProgressView.swift */; };
@@ -89,6 +88,32 @@
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 */; };
+ B55AFB5E2FE29BAC00A1A1A4 /* AppUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB5D2FE29BAC00A1A1A4 /* AppUtility.swift */; };
+ B55AFB6D2FE2A04C00A1A1A4 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B55AFB672FE2A04C00A1A1A4 /* NCEmptyView.xib */; };
+ B55AFB6E2FE2A04C00A1A1A4 /* MoEngageAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB5F2FE2A04C00A1A1A4 /* MoEngageAnalytics.swift */; };
+ B55AFB6F2FE2A04C00A1A1A4 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB642FE2A04C00A1A1A4 /* AppUpdater.swift */; };
+ B55AFB702FE2A04C00A1A1A4 /* TealiumHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB6B2FE2A04C00A1A1A4 /* TealiumHelper.swift */; };
+ B55AFB712FE2A04C00A1A1A4 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB622FE2A04C00A1A1A4 /* AnalyticsService.swift */; };
+ B55AFB722FE2A04C00A1A1A4 /* AnalyticsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB612FE2A04C00A1A1A4 /* AnalyticsHelper.swift */; };
+ B55AFB732FE2A04C00A1A1A4 /* NCEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB662FE2A04C00A1A1A4 /* NCEmptyDataSet.swift */; };
+ B55AFB742FE2A04C00A1A1A4 /* AdjustHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB6A2FE2A04C00A1A1A4 /* AdjustHelper.m */; };
+ B55AFB762FE2A0BD00A1A1A4 /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB752FE2A0BD00A1A1A4 /* NCLoginWeb.swift */; };
+ B55AFB7A2FE2A47500A1A1A4 /* LoginIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB792FE2A47500A1A1A4 /* LoginIntegrationTests.swift */; };
+ B55AFB7B2FE2A47500A1A1A4 /* BaseIntegrationXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB772FE2A47500A1A1A4 /* BaseIntegrationXCTestCase.swift */; };
+ B55AFB7C2FE2A47500A1A1A4 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB782FE2A47500A1A1A4 /* FilesIntegrationTests.swift */; };
+ B55AFB8A2FE2A49200A1A1A4 /* OnboardingTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB842FE2A49200A1A1A4 /* OnboardingTestCase.swift */; };
+ B55AFB8B2FE2A49200A1A1A4 /* RenameFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB862FE2A49200A1A1A4 /* RenameFileTests.swift */; };
+ B55AFB8C2FE2A49200A1A1A4 /* ScanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB872FE2A49200A1A1A4 /* ScanTests.swift */; };
+ B55AFB8D2FE2A49200A1A1A4 /* SettingsTestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB882FE2A49200A1A1A4 /* SettingsTestCases.swift */; };
+ B55AFB8E2FE2A49200A1A1A4 /* MoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB812FE2A49200A1A1A4 /* MoreTests.swift */; };
+ B55AFB8F2FE2A49200A1A1A4 /* SharingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB892FE2A49200A1A1A4 /* SharingTest.swift */; };
+ B55AFB902FE2A49200A1A1A4 /* CollaboraTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB7F2FE2A49200A1A1A4 /* CollaboraTestCase.swift */; };
+ B55AFB912FE2A49200A1A1A4 /* NCNotificationText.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB832FE2A49200A1A1A4 /* NCNotificationText.swift */; };
+ B55AFB922FE2A49200A1A1A4 /* E2EETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB802FE2A49200A1A1A4 /* E2EETests.swift */; };
+ B55AFB932FE2A49200A1A1A4 /* MoveAndCopyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB822FE2A49200A1A1A4 /* MoveAndCopyTests.swift */; };
+ B55AFB942FE2A49200A1A1A4 /* AudioUploadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB7E2FE2A49200A1A1A4 /* AudioUploadTests.swift */; };
+ B55AFB952FE2A49200A1A1A4 /* PrivacyPolicyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB852FE2A49200A1A1A4 /* PrivacyPolicyTest.swift */; };
+ B55AFB962FE2A49200A1A1A4 /* AssetUploadTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55AFB7D2FE2A49200A1A1A4 /* AssetUploadTest.swift */; };
CB3666201AF7550816B5CD6A /* NCContextMenuComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8932E90EC4278026D86CCCC9 /* NCContextMenuComment.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 */; };
@@ -1232,7 +1257,6 @@
AAA7BC2F2D3E3B83008F1A22 /* CapabilityResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapabilityResponse.swift; sourceTree = ""; };
AABD0C862D5F58C400F009E6 /* Server.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Server.sh; sourceTree = ""; };
AABD0C892D5F67A200F009E6 /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; };
- AABD0C9A2D5F73FA00F009E6 /* Placeholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Placeholder.swift; sourceTree = ""; };
AACCAB522CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Intent.strings; sourceTree = ""; };
AACCAB532CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = ""; };
AACCAB542CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; };
@@ -1279,6 +1303,33 @@
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
B4C7A5B36D1ED178FB6B76CB /* NCContextMenuPlayerTracks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenuPlayerTracks.swift; sourceTree = ""; };
+ B55AFB5D2FE29BAC00A1A1A4 /* AppUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUtility.swift; sourceTree = ""; };
+ B55AFB5F2FE2A04C00A1A1A4 /* MoEngageAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoEngageAnalytics.swift; sourceTree = ""; };
+ B55AFB612FE2A04C00A1A1A4 /* AnalyticsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsHelper.swift; sourceTree = ""; };
+ B55AFB622FE2A04C00A1A1A4 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; };
+ B55AFB642FE2A04C00A1A1A4 /* AppUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = ""; };
+ B55AFB662FE2A04C00A1A1A4 /* NCEmptyDataSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEmptyDataSet.swift; sourceTree = ""; };
+ B55AFB672FE2A04C00A1A1A4 /* NCEmptyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCEmptyView.xib; sourceTree = ""; };
+ B55AFB692FE2A04C00A1A1A4 /* AdjustHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AdjustHelper.h; sourceTree = ""; };
+ B55AFB6A2FE2A04C00A1A1A4 /* AdjustHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AdjustHelper.m; sourceTree = ""; };
+ B55AFB6B2FE2A04C00A1A1A4 /* TealiumHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TealiumHelper.swift; sourceTree = ""; };
+ B55AFB752FE2A0BD00A1A1A4 /* NCLoginWeb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginWeb.swift; sourceTree = ""; };
+ B55AFB772FE2A47500A1A1A4 /* BaseIntegrationXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseIntegrationXCTestCase.swift; sourceTree = ""; };
+ B55AFB782FE2A47500A1A1A4 /* FilesIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesIntegrationTests.swift; sourceTree = ""; };
+ B55AFB792FE2A47500A1A1A4 /* LoginIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginIntegrationTests.swift; sourceTree = ""; };
+ B55AFB7D2FE2A49200A1A1A4 /* AssetUploadTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetUploadTest.swift; sourceTree = ""; };
+ B55AFB7E2FE2A49200A1A1A4 /* AudioUploadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioUploadTests.swift; sourceTree = ""; };
+ B55AFB7F2FE2A49200A1A1A4 /* CollaboraTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollaboraTestCase.swift; sourceTree = ""; };
+ B55AFB802FE2A49200A1A1A4 /* E2EETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = E2EETests.swift; sourceTree = ""; };
+ B55AFB812FE2A49200A1A1A4 /* MoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreTests.swift; sourceTree = ""; };
+ B55AFB822FE2A49200A1A1A4 /* MoveAndCopyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveAndCopyTests.swift; sourceTree = ""; };
+ B55AFB832FE2A49200A1A1A4 /* NCNotificationText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNotificationText.swift; sourceTree = ""; };
+ B55AFB842FE2A49200A1A1A4 /* OnboardingTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTestCase.swift; sourceTree = ""; };
+ B55AFB852FE2A49200A1A1A4 /* PrivacyPolicyTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyTest.swift; sourceTree = ""; };
+ B55AFB862FE2A49200A1A1A4 /* RenameFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameFileTests.swift; sourceTree = ""; };
+ B55AFB872FE2A49200A1A1A4 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = ""; };
+ B55AFB882FE2A49200A1A1A4 /* SettingsTestCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTestCases.swift; sourceTree = ""; };
+ B55AFB892FE2A49200A1A1A4 /* SharingTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingTest.swift; sourceTree = ""; };
BB7697C94BA14450A0867940 /* NCContextMenuProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenuProfile.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; };
@@ -2102,6 +2153,19 @@
AA52EB442D42AC500089C348 /* NextcloudUnitTests */ = {
isa = PBXGroup;
children = (
+ B55AFB7D2FE2A49200A1A1A4 /* AssetUploadTest.swift */,
+ B55AFB7E2FE2A49200A1A1A4 /* AudioUploadTests.swift */,
+ B55AFB7F2FE2A49200A1A1A4 /* CollaboraTestCase.swift */,
+ B55AFB802FE2A49200A1A1A4 /* E2EETests.swift */,
+ B55AFB812FE2A49200A1A1A4 /* MoreTests.swift */,
+ B55AFB822FE2A49200A1A1A4 /* MoveAndCopyTests.swift */,
+ B55AFB832FE2A49200A1A1A4 /* NCNotificationText.swift */,
+ B55AFB842FE2A49200A1A1A4 /* OnboardingTestCase.swift */,
+ B55AFB852FE2A49200A1A1A4 /* PrivacyPolicyTest.swift */,
+ B55AFB862FE2A49200A1A1A4 /* RenameFileTests.swift */,
+ B55AFB872FE2A49200A1A1A4 /* ScanTests.swift */,
+ B55AFB882FE2A49200A1A1A4 /* SettingsTestCases.swift */,
+ B55AFB892FE2A49200A1A1A4 /* SharingTest.swift */,
F34BDB3B2F574A58007A222C /* BidiSafeFilenameTests.swift */,
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
);
@@ -2142,6 +2206,51 @@
path = Advanced;
sourceTree = "";
};
+ B55AFB602FE2A04C00A1A1A4 /* MoEngage */ = {
+ isa = PBXGroup;
+ children = (
+ B55AFB5F2FE2A04C00A1A1A4 /* MoEngageAnalytics.swift */,
+ );
+ path = MoEngage;
+ sourceTree = "";
+ };
+ B55AFB632FE2A04C00A1A1A4 /* Analytics */ = {
+ isa = PBXGroup;
+ children = (
+ B55AFB602FE2A04C00A1A1A4 /* MoEngage */,
+ B55AFB612FE2A04C00A1A1A4 /* AnalyticsHelper.swift */,
+ B55AFB622FE2A04C00A1A1A4 /* AnalyticsService.swift */,
+ );
+ path = Analytics;
+ sourceTree = "";
+ };
+ B55AFB652FE2A04C00A1A1A4 /* AppUpdate */ = {
+ isa = PBXGroup;
+ children = (
+ B55AFB642FE2A04C00A1A1A4 /* AppUpdater.swift */,
+ );
+ path = AppUpdate;
+ sourceTree = "";
+ };
+ B55AFB682FE2A04C00A1A1A4 /* EmptyView */ = {
+ isa = PBXGroup;
+ children = (
+ B55AFB662FE2A04C00A1A1A4 /* NCEmptyDataSet.swift */,
+ B55AFB672FE2A04C00A1A1A4 /* NCEmptyView.xib */,
+ );
+ path = EmptyView;
+ sourceTree = "";
+ };
+ B55AFB6C2FE2A04C00A1A1A4 /* Helper */ = {
+ isa = PBXGroup;
+ children = (
+ B55AFB692FE2A04C00A1A1A4 /* AdjustHelper.h */,
+ B55AFB6A2FE2A04C00A1A1A4 /* AdjustHelper.m */,
+ B55AFB6B2FE2A04C00A1A1A4 /* TealiumHelper.swift */,
+ );
+ path = Helper;
+ sourceTree = "";
+ };
C0046CDB2A17B98400D87C9D /* NextcloudUITests */ = {
isa = PBXGroup;
children = (
@@ -2159,7 +2268,9 @@
C04E2F212A17BB4D001BAD85 /* NextcloudIntegrationTests */ = {
isa = PBXGroup;
children = (
- AABD0C9A2D5F73FA00F009E6 /* Placeholder.swift */,
+ B55AFB772FE2A47500A1A1A4 /* BaseIntegrationXCTestCase.swift */,
+ B55AFB782FE2A47500A1A1A4 /* FilesIntegrationTests.swift */,
+ B55AFB792FE2A47500A1A1A4 /* LoginIntegrationTests.swift */,
);
path = NextcloudIntegrationTests;
sourceTree = "";
@@ -3004,6 +3115,7 @@
F7BFFA621A24D7300044ED85 /* Login */ = {
isa = PBXGroup;
children = (
+ B55AFB752FE2A0BD00A1A1A4 /* NCLoginWeb.swift */,
F702F2F025EE5CDA008F8E80 /* NCLogin.storyboard */,
F702F2F625EE5CEC008F8E80 /* NCLogin.swift */,
F745B252222D88AE00346520 /* NCLoginQRCode.swift */,
@@ -3350,6 +3462,11 @@
F7F67BAA1A24D27800EE80DA /* iOSClient */ = {
isa = PBXGroup;
children = (
+ B55AFB632FE2A04C00A1A1A4 /* Analytics */,
+ B55AFB652FE2A04C00A1A1A4 /* AppUpdate */,
+ B55AFB682FE2A04C00A1A1A4 /* EmptyView */,
+ B55AFB6C2FE2A04C00A1A1A4 /* Helper */,
+ B55AFB5D2FE29BAC00A1A1A4 /* AppUtility.swift */,
AA517BB42D66149900F8D37C /* .tx */,
F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */,
F7110ADF2F9773210095AA5C /* AppDelegate+AppRefresh.swift */,
@@ -4049,6 +4166,7 @@
F7362A1F220C853A005101B5 /* LaunchScreen.storyboard in Resources */,
F77444F622281649000D5EB0 /* NCMediaCell.xib in Resources */,
F78ACD4421903CF20088454D /* NCListCell.xib in Resources */,
+ B55AFB6D2FE2A04C00A1A1A4 /* NCEmptyView.xib in Resources */,
F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */,
F761856D29E98543006EB3B0 /* NCIntroCollectionViewCell.xib in Resources */,
F78ACD4621903D010088454D /* NCGridCell.xib in Resources */,
@@ -4250,6 +4368,19 @@
AA52EB472D42AC9E0089C348 /* Placeholder.swift in Sources */,
F34BDB3C2F574A58007A222C /* BidiSafeFilenameTests.swift in Sources */,
F372087D2BAB4C0F006B5430 /* TestConstants.swift in Sources */,
+ B55AFB8A2FE2A49200A1A1A4 /* OnboardingTestCase.swift in Sources */,
+ B55AFB8B2FE2A49200A1A1A4 /* RenameFileTests.swift in Sources */,
+ B55AFB8C2FE2A49200A1A1A4 /* ScanTests.swift in Sources */,
+ B55AFB8D2FE2A49200A1A1A4 /* SettingsTestCases.swift in Sources */,
+ B55AFB8E2FE2A49200A1A1A4 /* MoreTests.swift in Sources */,
+ B55AFB8F2FE2A49200A1A1A4 /* SharingTest.swift in Sources */,
+ B55AFB902FE2A49200A1A1A4 /* CollaboraTestCase.swift in Sources */,
+ B55AFB912FE2A49200A1A1A4 /* NCNotificationText.swift in Sources */,
+ B55AFB922FE2A49200A1A1A4 /* E2EETests.swift in Sources */,
+ B55AFB932FE2A49200A1A1A4 /* MoveAndCopyTests.swift in Sources */,
+ B55AFB942FE2A49200A1A1A4 /* AudioUploadTests.swift in Sources */,
+ B55AFB952FE2A49200A1A1A4 /* PrivacyPolicyTest.swift in Sources */,
+ B55AFB962FE2A49200A1A1A4 /* AssetUploadTest.swift in Sources */,
F78E2D6C29AF02DB0024D4F3 /* Database.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4264,6 +4395,9 @@
AA74AA972D3172D100BE3458 /* UITestError.swift in Sources */,
AA3C85E82D36B08C00F74F12 /* UITestBackend.swift in Sources */,
AA8E03DC2D2FBAC200E7E89C /* DownloadLimitUITests.swift in Sources */,
+ B55AFB7A2FE2A47500A1A1A4 /* LoginIntegrationTests.swift in Sources */,
+ B55AFB7B2FE2A47500A1A1A4 /* BaseIntegrationXCTestCase.swift in Sources */,
+ B55AFB7C2FE2A47500A1A1A4 /* FilesIntegrationTests.swift in Sources */,
AABD0C8A2D5F67A400F009E6 /* XCUIElement.swift in Sources */,
AA3C85EE2D36BCCE00F74F12 /* SharesResponse.swift in Sources */,
AAA7BC2E2D3E39F1008F1A22 /* CapabilitiesResponse.swift in Sources */,
@@ -4271,7 +4405,6 @@
F3374A962D6744A4002A38F9 /* BaseUIXCTestCase.swift in Sources */,
F37208812BAB5979006B5430 /* TestConstants.swift in Sources */,
F33D303E2D8B129600531D64 /* AutoUploadUITests.swift in Sources */,
- AABD0C9B2D5F73FC00F009E6 /* Placeholder.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4668,6 +4801,13 @@
AA8D31682D41224800FE2775 /* NCShareToggleCell.swift in Sources */,
F7A846DE2BB01ACB0024816F /* NCTrashCellProtocol.swift in Sources */,
F7236DDF2F7C00E600F9D722 /* HudIndeterminateBannerView.swift in Sources */,
+ B55AFB6E2FE2A04C00A1A1A4 /* MoEngageAnalytics.swift in Sources */,
+ B55AFB6F2FE2A04C00A1A1A4 /* AppUpdater.swift in Sources */,
+ B55AFB702FE2A04C00A1A1A4 /* TealiumHelper.swift in Sources */,
+ B55AFB712FE2A04C00A1A1A4 /* AnalyticsService.swift in Sources */,
+ B55AFB722FE2A04C00A1A1A4 /* AnalyticsHelper.swift in Sources */,
+ B55AFB732FE2A04C00A1A1A4 /* NCEmptyDataSet.swift in Sources */,
+ B55AFB742FE2A04C00A1A1A4 /* AdjustHelper.m in Sources */,
F799DF852C4B7E56003410B5 /* NCSectionHeader.swift in Sources */,
F78A10BF29322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
F7743A122C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift in Sources */,
@@ -4825,6 +4965,7 @@
AAFC0D062F9AA10000F0A001 /* NCFocusedAutoUploadScreenDimmer.swift in Sources */,
AAFC0D092F9AA10000F0A001 /* NCFocusedAutoUploadCloudAnimation.swift in Sources */,
AAFC0D0B2F9AA10000F0A001 /* NCAutoUploadCounter.swift in Sources */,
+ F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift in Sources */,
F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */,
F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
F75D19E325EFE09000D74598 /* NCContextMenuTrash.swift in Sources */,
@@ -4883,6 +5024,7 @@
F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */,
F7865FF12F39D32F00D09AE4 /* NCCollectionViewCommon+Search.swift in Sources */,
F7327E352B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift in Sources */,
+ B55AFB762FE2A0BD00A1A1A4 /* NCLoginWeb.swift in Sources */,
F76687072B7D067400779E3F /* NCAudioRecorderViewController.swift in Sources */,
AA8E03DA2D2ED83300E7E89C /* TransientShare.swift in Sources */,
F7A03E2F2D425A14007AA677 /* NCFavoriteNavigationController.swift in Sources */,
@@ -4909,6 +5051,7 @@
F7DA0F672F66AA0D0033020C /* ShowBanner.swift in Sources */,
F752BA052E58C05200616A26 /* Maintenance.swift in Sources */,
F763D29D2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
+ B55AFB5E2FE29BAC00A1A1A4 /* AppUtility.swift in Sources */,
F76882252C0DD1E7001CF441 /* NCSettingsAdvancedModel.swift in Sources */,
F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
diff --git a/Tests/Common/BaseXCTestCase.swift b/Tests/Common/BaseXCTestCase.swift
new file mode 100644
index 0000000000..5c03bc5384
--- /dev/null
+++ b/Tests/Common/BaseXCTestCase.swift
@@ -0,0 +1,38 @@
+//
+// BaseXCTestCase.swift
+// Nextcloud
+//
+// Created by Milen on 20.03.24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+import Foundation
+import UIKit
+import Alamofire
+import NextcloudKit
+@testable import Nextcloud
+
+class BaseXCTestCase: XCTestCase {
+ var appToken = ""
+
+ func setupAppToken() {
+ let expectation = expectation(description: "Should get app token")
+
+ NextcloudKit.shared.getAppPassword(url: TestConstants.server, user: TestConstants.username, password: TestConstants.password) { token, _, error in
+ XCTAssertEqual(error.errorCode, 0)
+ XCTAssertNotNil(token)
+
+ guard let token else { return XCTFail() }
+
+ self.appToken = token
+ expectation.fulfill()
+ }
+
+ waitForExpectations(timeout: TestConstants.timeoutLong)
+ }
+
+ override func setUpWithError() throws {
+ setupAppToken()
+ }
+}
diff --git a/Tests/Common/TestConstants.swift b/Tests/Common/TestConstants.swift
new file mode 100644
index 0000000000..5022e70803
--- /dev/null
+++ b/Tests/Common/TestConstants.swift
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2023 Marcel Müller
+//
+// Author Marcel Müller
+//
+// GNU GPL version 3 or any later version
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import Foundation
+import UIKit
+
+///
+/// Immutable test configuration.
+///
+enum TestConstants {
+ ///
+ /// The default number of seconds to wait for the appearance of user interface controls during user interface tests.
+ ///
+ static let controlExistenceTimeout: Double = 10
+
+ ///
+ /// The full base URL for the server to run against.
+ ///
+ static let server = "http://localhost:8080"
+
+ ///
+ /// Default user name to sign in with.
+ ///
+ static let username = "admin"
+
+ ///
+ /// Password of the default user name to sign in with.
+ ///
+ static let password = "admin"
+
+ ///
+ /// Account identifier of the default user to test with.
+ ///
+ static let account = "\(username) \(server)"
+}
diff --git a/Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift b/Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift
new file mode 100644
index 0000000000..703aa37f07
--- /dev/null
+++ b/Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift
@@ -0,0 +1,29 @@
+//
+// BaseIntegrationXCTestCase.swift
+//
+//
+// Created by Milen Pivchev on 20.06.23.
+// Copyright © 2023 Milen Pivchev. All rights reserved.
+//
+// Author: Milen Pivchev
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+class BaseIntegrationXCTestCase: BaseXCTestCase {
+ var randomInt: Int {
+ get {
+ return Int.random(in: 1000...Int.max)
+ }
+ }
+}
diff --git a/Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift b/Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
new file mode 100644
index 0000000000..c1bcbca235
--- /dev/null
+++ b/Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
@@ -0,0 +1,94 @@
+//
+// NextcloudIntegrationTests.swift
+// NextcloudIntegrationTests
+//
+// Created by Milen Pivchev on 5/19/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import XCTest
+import NextcloudKit
+@testable import Nextcloud
+
+final class FilesIntegrationTests: BaseIntegrationXCTestCase {
+
+ override func setUp() {
+ NCAccount().deleteAllAccounts()
+ }
+
+// func test_createReadDeleteFolder_withProperParams_shouldCreateReadDeleteFolder() throws {
+// let expectation = expectation(description: "Should finish last callback")
+//
+// let folderName = "TestFolder\(randomInt)"
+// let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)"
+// let serverUrlFileName = "\(serverUrl)/\(folderName)"
+//
+// NCSession.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username)
+// let session = NCSession.shared.getSession(account: TestConstants.account)
+//
+// NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+// NextcloudKit.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: appToken, userAgent: userAgent, nextcloudVersion: 0, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
+//
+// // Test creating folder
+// NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, overwrite: true, withPush: true, metadata: nil, sceneIdentifier: nil, session: session ) { error in
+//
+// XCTAssertEqual(NKError.success.errorCode, error.errorCode)
+// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
+//
+// Thread.sleep(forTimeInterval: 1)
+//
+// // Test reading folder, should exist
+// NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: TestConstants.username, checkResponseDataChanged: false, queue: .main) { account, metadataFolder, _, _, _ in
+//
+// XCTAssertEqual(TestConstants.account, account)
+// XCTAssertEqual(NKError.success.errorCode, error.errorCode)
+// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
+// XCTAssertEqual(metadataFolder?.fileName, folderName)
+//
+// // Check Realm directory, should exist
+// let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
+// XCTAssertNotNil(directory)
+//
+// Thread.sleep(forTimeInterval: 1)
+//
+// Task {
+// // Test deleting folder
+// await _ = NCNetworking.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName, account: TestConstants.account)
+//
+// XCTAssertEqual(NKError.success.errorCode, error.errorCode)
+// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
+//
+// try await Task.sleep(for: .seconds(1))
+//
+// // Test reading folder, should NOT exist
+// NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: TestConstants.username, checkResponseDataChanged: false, queue: .main) { account, metadataFolder, _, _, _ in
+//
+// defer { expectation.fulfill() }
+//
+// XCTAssertEqual(0, error.errorCode)
+// XCTAssertNil(metadataFolder?.fileName)
+//
+// // Check Realm directory, should NOT exist
+// let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
+// XCTAssertNil(directory)
+// }
+// }
+// }
+// }
+//
+// waitForExpectations(timeout: TestConstants.timeoutLong)
+// }
+}
diff --git a/Tests/NextcloudIntegrationTests/Placeholder.swift b/Tests/NextcloudIntegrationTests/Placeholder.swift
deleted file mode 100644
index 6cc0aa6c4b..0000000000
--- a/Tests/NextcloudIntegrationTests/Placeholder.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: Nextcloud GmbH
-// SPDX-FileCopyrightText: 2025 Iva Horn
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-// There are no unit tests yet.
-// This is an empty placeholder file.
-// The compiler requires at least an empty file for every target.
diff --git a/Tests/NextcloudUITests/AssistantUITests.swift b/Tests/NextcloudUITests/AssistantUITests.swift
index 6fffeaee40..bce341aed9 100644
--- a/Tests/NextcloudUITests/AssistantUITests.swift
+++ b/Tests/NextcloudUITests/AssistantUITests.swift
@@ -1,153 +1,153 @@
-//// SPDX-FileCopyrightText: Nextcloud GmbH
-//// SPDX-FileCopyrightText: 2025 Iva Horn
-//// SPDX-License-Identifier: GPL-3.0-or-later
-//
-//import XCTest
-//
-/////
-///// User interface tests for the download limits management on shares.
-/////
-//@MainActor
-//final class AssistantUITests: BaseUIXCTestCase {
-// let taskInputCreated = "TestTaskCreated" + UUID().uuidString
-// let taskInputRetried = "TestTaskRetried" + UUID().uuidString
-// let taskInputToEdit = "TestTaskToEdit" + UUID().uuidString
-// let taskInputDeleted = "TestTaskDeleted" + UUID().uuidString
-//
-// // MARK: - Lifecycle
-//
-// override func setUp() async throws {
-// try await super.setUp()
-// continueAfterFailure = false
-//
-// // Handle alerts presented by the system.
-// addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
-// addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
-//
-// // Launch the app.
-// app = XCUIApplication()
-// app.launchArguments = ["UI_TESTING"]
-// app.launch()
-//
-// try await logIn()
-//
-// // Set up test backend communication.
-// backend = UITestBackend()
-//
-// try await backend.assertCapability(true, capability: \.assistant)
-// }
-//
-// ///
-// /// Leads to the Assistant screen.
-// ///
-// private func goToAssistant() {
-// let button = app.tabBars["Tab Bar"].buttons["More"]
-// guard button.await() else { return }
-// button.tap()
-//
-// let talkStaticText = app.tables.staticTexts["Assistant"]
-// talkStaticText.tap()
-// }
-//
-// private func createTask(input: String) {
-// app.navigationBars["Assistant"].buttons["ConversationsButton"].tap()
-//
-// let inputTextEditor = app.textViews["InputTextEditor"]
-// inputTextEditor.await()
-// inputTextEditor.typeText(input)
-// app.navigationBars["New Free text to text prompt task"].buttons["Create"].tap()
-// }
-//
-// private func retryTask() {
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// cell.staticTexts[taskInputRetried].press(forDuration: 2);
-//
-// let retryButton = app.buttons["TaskRetryContextMenu"]
-// XCTAssertTrue(retryButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
-// retryButton.tap()
-// }
-//
-// private func editTask() {
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// cell.staticTexts[taskInputToEdit].press(forDuration: 2);
-//
-// let editButton = app.buttons["TaskEditContextMenu"]
-// XCTAssertTrue(editButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
-// editButton.tap()
-//
-// app.textViews["InputTextEditor"].typeText("Edited")
-// app.navigationBars["Edit Free text to text prompt task"].buttons["Edit"].tap()
-// }
-//
-// private func deleteTask() {
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// cell.staticTexts[taskInputDeleted].press(forDuration: 2);
-//
-// let deleteButton = app.buttons["TaskDeleteContextMenu"]
-// XCTAssertTrue(deleteButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
-// deleteButton.tap()
-//
-// app.sheets.scrollViews.otherElements.buttons["Delete"].tap()
-// }
-//
-// // MARK: - Tests
-//
-// func testCreateAssistantTask() async throws {
-// goToAssistant()
-//
-// createTask(input: taskInputCreated)
-//
-// pullToRefresh()
-//
-// try await aMoment()
-//
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// XCTAssert(cell.staticTexts[taskInputCreated].exists)
-// }
-//
-// func testRetryAssistantTask() async throws {
-// goToAssistant()
-//
-// createTask(input: taskInputRetried)
-//
-// retryTask()
-//
-// pullToRefresh()
-//
-// try await aMoment()
-//
-// let matchingElements = app.collectionViews.cells.staticTexts.matching(identifier: taskInputRetried)
-// print(app.collectionViews.staticTexts.debugDescription)
-// XCTAssertEqual(matchingElements.count, 2, "Expected 2 elements")
-// }
-//
-// func testEditAssistantTask() async throws {
-// goToAssistant()
-//
-// createTask(input: taskInputToEdit)
-//
-// editTask()
-//
-// pullToRefresh()
-//
-// try await aMoment()
-//
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// XCTAssert(cell.staticTexts[taskInputToEdit + "Edited"].exists)
-// }
-//
-// func testDeleteAssistantTask() async throws {
-// goToAssistant()
-//
-// createTask(input: taskInputDeleted)
-//
-// deleteTask()
-//
-// pullToRefresh()
-//
-// try await aMoment()
-//
-// let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
-// XCTAssert(!cell.staticTexts[taskInputDeleted].exists)
-// }
-//}
+// SPDX-FileCopyrightText: Nextcloud GmbH
+// SPDX-FileCopyrightText: 2025 Iva Horn
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import XCTest
+
+///
+/// User interface tests for the download limits management on shares.
+///
+@MainActor
+final class AssistantUITests: BaseUIXCTestCase {
+ let taskInputCreated = "TestTaskCreated" + UUID().uuidString
+ let taskInputRetried = "TestTaskRetried" + UUID().uuidString
+ let taskInputToEdit = "TestTaskToEdit" + UUID().uuidString
+ let taskInputDeleted = "TestTaskDeleted" + UUID().uuidString
+
+ // MARK: - Lifecycle
+
+ override func setUp() async throws {
+ try await super.setUp()
+ continueAfterFailure = false
+
+ // Handle alerts presented by the system.
+ addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
+ addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
+
+ // Launch the app.
+ app = XCUIApplication()
+ app.launchArguments = ["UI_TESTING"]
+ app.launch()
+
+ try await logIn()
+
+ // Set up test backend communication.
+ backend = UITestBackend()
+
+ try await backend.assertCapability(true, capability: \.assistant)
+ }
+
+ ///
+ /// Leads to the Assistant screen.
+ ///
+ private func goToAssistant() {
+ let button = app.tabBars["Tab Bar"].buttons["More"]
+ guard button.await() else { return }
+ button.tap()
+
+ let talkStaticText = app.tables.staticTexts["Assistant"]
+ talkStaticText.tap()
+ }
+
+ private func createTask(input: String) {
+ app.navigationBars["Assistant"].buttons["CreateButton"].tap()
+
+ let inputTextEditor = app.textViews["InputTextEditor"]
+ inputTextEditor.await()
+ inputTextEditor.typeText(input)
+ app.navigationBars["New Free text to text prompt task"].buttons["Create"].tap()
+ }
+
+ private func retryTask() {
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ cell.staticTexts[taskInputRetried].press(forDuration: 2);
+
+ let retryButton = app.buttons["TaskRetryContextMenu"]
+ XCTAssertTrue(retryButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
+ retryButton.tap()
+ }
+
+ private func editTask() {
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ cell.staticTexts[taskInputToEdit].press(forDuration: 2);
+
+ let editButton = app.buttons["TaskEditContextMenu"]
+ XCTAssertTrue(editButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
+ editButton.tap()
+
+ app.textViews["InputTextEditor"].typeText("Edited")
+ app.navigationBars["Edit Free text to text prompt task"].buttons["Edit"].tap()
+ }
+
+ private func deleteTask() {
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ cell.staticTexts[taskInputDeleted].press(forDuration: 2);
+
+ let deleteButton = app.buttons["TaskDeleteContextMenu"]
+ XCTAssertTrue(deleteButton.waitForExistence(timeout: 2), "Edit button not found in context menu")
+ deleteButton.tap()
+
+ app.sheets.scrollViews.otherElements.buttons["Delete"].tap()
+ }
+
+ // MARK: - Tests
+
+ func testCreateAssistantTask() async throws {
+ goToAssistant()
+
+ createTask(input: taskInputCreated)
+
+ pullToRefresh()
+
+ try await aMoment()
+
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ XCTAssert(cell.staticTexts[taskInputCreated].exists)
+ }
+
+ func testRetryAssistantTask() async throws {
+ goToAssistant()
+
+ createTask(input: taskInputRetried)
+
+ retryTask()
+
+ pullToRefresh()
+
+ try await aMoment()
+
+ let matchingElements = app.collectionViews.cells.staticTexts.matching(identifier: taskInputRetried)
+ print(app.collectionViews.staticTexts.debugDescription)
+ XCTAssertEqual(matchingElements.count, 2, "Expected 2 elements")
+ }
+
+ func testEditAssistantTask() async throws {
+ goToAssistant()
+
+ createTask(input: taskInputToEdit)
+
+ editTask()
+
+ pullToRefresh()
+
+ try await aMoment()
+
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ XCTAssert(cell.staticTexts[taskInputToEdit + "Edited"].exists)
+ }
+
+ func testDeleteAssistantTask() async throws {
+ goToAssistant()
+
+ createTask(input: taskInputDeleted)
+
+ deleteTask()
+
+ pullToRefresh()
+
+ try await aMoment()
+
+ let cell = app.collectionViews.children(matching: .cell).element(boundBy: 0)
+ XCTAssert(!cell.staticTexts[taskInputDeleted].exists)
+ }
+}
diff --git a/Tests/NextcloudUITests/AutoUploadUITests.swift b/Tests/NextcloudUITests/AutoUploadUITests.swift
index 414a77cf08..8c487e6f28 100644
--- a/Tests/NextcloudUITests/AutoUploadUITests.swift
+++ b/Tests/NextcloudUITests/AutoUploadUITests.swift
@@ -1,127 +1,127 @@
-//// SPDX-FileCopyrightText: Nextcloud GmbH
-//// SPDX-FileCopyrightText: 2025 Iva Horn
-//// SPDX-License-Identifier: GPL-3.0-or-later
-//
-//import XCTest
-//
-/////
-///// User interface tests for the download limits management on shares.
-/////
-//@MainActor
-//final class AutoUploadUITests: BaseUIXCTestCase {
-// // MARK: - Lifecycle
-// override func setUp() async throws {
-// try await super.setUp()
-// continueAfterFailure = false
-//
-// // Handle alerts presented by the system.
-// addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
-// addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
-//
-// // Launch the app.
-// app = XCUIApplication()
-// app.launchArguments = ["UI_TESTING"]
-// app.launch()
-//
-// try await logIn()
-//
-// // Set up test backend communication.
-// backend = UITestBackend()
-// }
-//
-// private func goToAutoUpload() async throws {
-// addUIInterruptionMonitor(withDescription: "Are you sure you want to upload all photos?", for: "Confirm")
-//
-// app.tabBars["Tab Bar"].buttons["More"].tap()
-//
-// app.tables/*@START_MENU_TOKEN@*/.staticTexts["Settings"]/*[[".cells.staticTexts[\"Settings\"]",".staticTexts[\"Settings\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
-//
-// let collectionViewsQuery = app.collectionViews
-// collectionViewsQuery/*@START_MENU_TOKEN@*/.staticTexts["Auto upload"]/*[[".cells",".buttons[\"Auto upload\"].staticTexts[\"Auto upload\"]",".staticTexts[\"Auto upload\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/.tap()
-//
-// try await aSmallMoment()
-//
-// let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
-// let allowButton = springboard.buttons["Allow Full Access"]
-//
-// if allowButton.await() {
-// allowButton.tap()
-// }
-// }
-//
-// func testAutoUploadAllPhotos() async throws {
-// try await goToAutoUpload()
-//
-// let collectionViewsQuery = app.collectionViews
-//
-// let turnOnAutoUploadingSwitch = collectionViewsQuery.switches["Turn on auto uploading"]
-//
-// collectionViewsQuery/*@START_MENU_TOKEN@*/.switches["Auto upload photos"]/*[[".cells.switches[\"Auto upload photos\"]",".switches[\"Auto upload photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.swipeUp()
-//
-// if turnOnAutoUploadingSwitch.await() {
-// turnOnAutoUploadingSwitch.tap()
-// }
-//
-// app.tabBars["Tab Bar"].buttons["Files"].tap()
-//
-// try await aSmallMoment()
-//
-// pullToRefresh()
-//
-// let photosItem = app.collectionViews["NCCollectionViewCommon"]/*@START_MENU_TOKEN@*/.staticTexts["Photos"]/*[[".cells[\"Photos\"].staticTexts[\"Photos\"]",".cells[\"Cell\/Photos\"].staticTexts[\"Photos\"]",".staticTexts[\"Photos\"]"],[[[-1,2],[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
-// if photosItem.await() {
-// photosItem.tap()
-// }
-//
-// try await aSmallMoment()
-//
-// XCTAssertTrue(app.collectionViews.cells.count == 6)
-// }
-//
-// func testAutoUploadNewPhotos() async throws {
-// try await goToAutoUpload()
-//
-// let backUpNewPhotosVideosOnlySwitch = app.collectionViews.switches["NewPhotosToggle"]
-//
-// if backUpNewPhotosVideosOnlySwitch.await() {
-// backUpNewPhotosVideosOnlySwitch.switches.firstMatch.tap()
-// }
-//
-// let collectionViewsQuery = app.collectionViews
-//
-// let turnOnAutoUploadingSwitch = collectionViewsQuery.switches["Turn on auto uploading"]
-//
-// collectionViewsQuery/*@START_MENU_TOKEN@*/.switches["Auto upload photos"]/*[[".cells.switches[\"Auto upload photos\"]",".switches[\"Auto upload photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.swipeUp()
-//
-// if turnOnAutoUploadingSwitch.await() {
-// turnOnAutoUploadingSwitch.tap()
-// }
-//
-// app.tabBars["Tab Bar"].buttons["Files"].tap()
-//
-// try await aSmallMoment()
-//
-// pullToRefresh()
-//
-// let photosItem = app.collectionViews["NCCollectionViewCommon"]/*@START_MENU_TOKEN@*/.staticTexts["Photos"]/*[[".cells[\"Photos\"].staticTexts[\"Photos\"]",".cells[\"Cell\/Photos\"].staticTexts[\"Photos\"]",".staticTexts[\"Photos\"]"],[[[-1,2],[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
-//
-// // Does not seem possible to take a screenshot on Simulator or easily transfer a new photo in Simulator.
-// // Thus for now we can only rely on the Photos folder not existing at all to test this.
-// XCTAssertFalse(photosItem.exists)
-// }
-//
-// override func tearDown() async throws {
-// let tabBar = app.tabBars["Tab Bar"]
-// tabBar.buttons["Files"].tap()
-// let nccollectionviewcommonCollectionView = app.collectionViews["NCCollectionViewCommon"]
-// let cell = nccollectionviewcommonCollectionView/*@START_MENU_TOKEN@*/.cells["Cell/Photos"]/*[[".cells[\"Photos\"]",".cells[\"Cell\/Photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
-// if !cell.exists { return }
-//
-// cell.otherElements.containing(.button, identifier:"Cell/Photos/shareButton").children(matching: .button).element(boundBy: 1).tap()
-// let tablesQuery = app.tables
-// tablesQuery/*@START_MENU_TOKEN@*/.staticTexts["Delete folder"]/*[[".cells.staticTexts[\"Delete folder\"]",".staticTexts[\"Delete folder\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
-// app.alerts["Delete folder?"].scrollViews.otherElements.buttons["Yes"].tap()
-//
-// cell.awaitInexistence()
-// }
-//}
+// SPDX-FileCopyrightText: Nextcloud GmbH
+// SPDX-FileCopyrightText: 2025 Iva Horn
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import XCTest
+
+///
+/// User interface tests for the download limits management on shares.
+///
+@MainActor
+final class AutoUploadUITests: BaseUIXCTestCase {
+ // MARK: - Lifecycle
+ override func setUp() async throws {
+ try await super.setUp()
+ continueAfterFailure = false
+
+ // Handle alerts presented by the system.
+ addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
+ addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
+
+ // Launch the app.
+ app = XCUIApplication()
+ app.launchArguments = ["UI_TESTING"]
+ app.launch()
+
+ try await logIn()
+
+ // Set up test backend communication.
+ backend = UITestBackend()
+ }
+
+ private func goToAutoUpload() async throws {
+ addUIInterruptionMonitor(withDescription: "Are you sure you want to upload all photos?", for: "Confirm")
+
+ app.tabBars["Tab Bar"].buttons["More"].tap()
+
+ app.tables/*@START_MENU_TOKEN@*/.staticTexts["Settings"]/*[[".cells.staticTexts[\"Settings\"]",".staticTexts[\"Settings\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
+
+ let collectionViewsQuery = app.collectionViews
+ collectionViewsQuery/*@START_MENU_TOKEN@*/.staticTexts["Auto upload"]/*[[".cells",".buttons[\"Auto upload\"].staticTexts[\"Auto upload\"]",".staticTexts[\"Auto upload\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/.tap()
+
+ try await aSmallMoment()
+
+ let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
+ let allowButton = springboard.buttons["Allow Full Access"]
+
+ if allowButton.await() {
+ allowButton.tap()
+ }
+ }
+
+ func testAutoUploadAllPhotos() async throws {
+ try await goToAutoUpload()
+
+ let collectionViewsQuery = app.collectionViews
+
+ let turnOnAutoUploadingSwitch = collectionViewsQuery.switches["Turn on auto uploading"]
+
+ collectionViewsQuery/*@START_MENU_TOKEN@*/.switches["Auto upload photos"]/*[[".cells.switches[\"Auto upload photos\"]",".switches[\"Auto upload photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.swipeUp()
+
+ if turnOnAutoUploadingSwitch.await() {
+ turnOnAutoUploadingSwitch.tap()
+ }
+
+ app.tabBars["Tab Bar"].buttons["Files"].tap()
+
+ try await aSmallMoment()
+
+ pullToRefresh()
+
+ let photosItem = app.collectionViews["NCCollectionViewCommon"]/*@START_MENU_TOKEN@*/.staticTexts["Photos"]/*[[".cells[\"Photos\"].staticTexts[\"Photos\"]",".cells[\"Cell\/Photos\"].staticTexts[\"Photos\"]",".staticTexts[\"Photos\"]"],[[[-1,2],[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
+ if photosItem.await() {
+ photosItem.tap()
+ }
+
+ try await aSmallMoment()
+
+ XCTAssertTrue(app.collectionViews.cells.count == 6)
+ }
+
+ func testAutoUploadNewPhotos() async throws {
+ try await goToAutoUpload()
+
+ let backUpNewPhotosVideosOnlySwitch = app.collectionViews.switches["NewPhotosToggle"]
+
+ if backUpNewPhotosVideosOnlySwitch.await() {
+ backUpNewPhotosVideosOnlySwitch.switches.firstMatch.tap()
+ }
+
+ let collectionViewsQuery = app.collectionViews
+
+ let turnOnAutoUploadingSwitch = collectionViewsQuery.switches["Turn on auto uploading"]
+
+ collectionViewsQuery/*@START_MENU_TOKEN@*/.switches["Auto upload photos"]/*[[".cells.switches[\"Auto upload photos\"]",".switches[\"Auto upload photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.swipeUp()
+
+ if turnOnAutoUploadingSwitch.await() {
+ turnOnAutoUploadingSwitch.tap()
+ }
+
+ app.tabBars["Tab Bar"].buttons["Files"].tap()
+
+ try await aSmallMoment()
+
+ pullToRefresh()
+
+ let photosItem = app.collectionViews["NCCollectionViewCommon"]/*@START_MENU_TOKEN@*/.staticTexts["Photos"]/*[[".cells[\"Photos\"].staticTexts[\"Photos\"]",".cells[\"Cell\/Photos\"].staticTexts[\"Photos\"]",".staticTexts[\"Photos\"]"],[[[-1,2],[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
+
+ // Does not seem possible to take a screenshot on Simulator or easily transfer a new photo in Simulator.
+ // Thus for now we can only rely on the Photos folder not existing at all to test this.
+ XCTAssertFalse(photosItem.exists)
+ }
+
+ override func tearDown() async throws {
+ let tabBar = app.tabBars["Tab Bar"]
+ tabBar.buttons["Files"].tap()
+ let nccollectionviewcommonCollectionView = app.collectionViews["NCCollectionViewCommon"]
+ let cell = nccollectionviewcommonCollectionView/*@START_MENU_TOKEN@*/.cells["Cell/Photos"]/*[[".cells[\"Photos\"]",".cells[\"Cell\/Photos\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
+ if !cell.exists { return }
+
+ cell.otherElements.containing(.button, identifier:"Cell/Photos/shareButton").children(matching: .button).element(boundBy: 1).tap()
+ let tablesQuery = app.tables
+ tablesQuery/*@START_MENU_TOKEN@*/.staticTexts["Delete folder"]/*[[".cells.staticTexts[\"Delete folder\"]",".staticTexts[\"Delete folder\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
+ app.alerts["Delete folder?"].scrollViews.otherElements.buttons["Yes"].tap()
+
+ cell.awaitInexistence()
+ }
+}
diff --git a/Tests/NextcloudUITests/DownloadLimitUITests.swift b/Tests/NextcloudUITests/DownloadLimitUITests.swift
index f0e80ac54d..975fe1f865 100644
--- a/Tests/NextcloudUITests/DownloadLimitUITests.swift
+++ b/Tests/NextcloudUITests/DownloadLimitUITests.swift
@@ -1,484 +1,484 @@
-//// SPDX-FileCopyrightText: Nextcloud GmbH
-//// SPDX-FileCopyrightText: 2025 Iva Horn
-//// SPDX-License-Identifier: GPL-3.0-or-later
-//
-//import XCTest
-//
-/////
-///// User interface tests for the download limits management on shares.
-/////
-//@MainActor
-//final class DownloadLimitUITests: BaseUIXCTestCase {
-// ///
-// /// Name of the file to work with.
-// ///
-// /// The leading underscore is required for the file to appear at the top of the list.
-// /// Obviously, this is fragile by making some assumptions of the user interface state.
-// ///
-// let testFileName = "_Xcode UI Test Subject.md"
-//
-// // MARK: - Lifecycle
-//
-// override func setUp() async throws {
-// try await super.setUp()
-// continueAfterFailure = false
-//
-// // Handle alerts presented by the system.
-// addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
-// addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
-//
-// // Launch the app.
-// app = XCUIApplication()
-// app.launchArguments = ["UI_TESTING"]
-// app.launch()
-//
-// try await logIn()
-//
-// // Set up test backend communication.
-// backend = UITestBackend()
-//
-// try await backend.assertCapability(true, capability: \.downloadLimit)
-// try await backend.delete(testFileName)
-// try await backend.prepareTestFile(testFileName)
-// }
-//
-// // MARK: - Tests
-//
-// func testShareWithoutDownloadLimitCapability() async throws {
-// // This cannot be implemented at the time of writing.
-// // There is no way to disable and enable server apps via web API.
-// // The Xcode UI test process cannot access Docker.
-// throw XCTSkip("Not implemented yet!")
-// }
-//
-// func testNewShareWithoutDownloadLimit() async throws {
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-//
-// guard shareButton.exists else {
-// throw UITestError.waitForExistence(shareButton)
-// }
-//
-// shareButton.tap()
-//
-// // Tap add share link button.
-//
-// let addShareLinkButton = app.buttons["addShareLink"]
-// guard addShareLinkButton.await() else { return }
-// addShareLinkButton.tap()
-//
-// // Tap confirm share button.
-//
-// let confirmShareButton = app.buttons["confirmShare"]
-//
-// guard confirmShareButton.exists else {
-// throw UITestError.waitForExistence(confirmShareButton)
-// }
-//
-// confirmShareButton.tap()
-// confirmShareButton.awaitInexistence()
-//
-// // Then
-// let shares = try await backend.getShares(byPath: "/\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await backend.assertNoDownloadLimit(by: token)
-// }
-//
-// func testNewShareWithDownloadLimit() async throws {
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap add share link button.
-//
-// let addShareLinkButton = app.buttons["addShareLink"]
-// guard addShareLinkButton.await() else { return }
-// addShareLinkButton.tap()
-//
-// // Tap download limits.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// guard downloadLimitCell.await() else { return }
-// downloadLimitCell.tap()
-//
-// // Tap download limit switch.
-//
-// let downloadLimitSwitch = app.switches["downloadLimitSwitch"]
-// guard downloadLimitSwitch.await() else { return }
-// downloadLimitSwitch.tap()
-//
-// // Update allowed downloads.
-//
-// let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
-// guard allowedDownloadsTextField.await() else { return }
-// allowedDownloadsTextField.tap()
-// allowedDownloadsTextField.typeText("3")
-//
-// // Tap navigation back button.
-//
-// app.navigationBars.buttons.firstMatch.tap()
-//
-// // Tap confirm share button.
-//
-// let confirmShareButton = app.buttons["confirmShare"]
-// guard confirmShareButton.await() else { return }
-// confirmShareButton.tap()
-// confirmShareButton.awaitInexistence()
-//
-// // Then
-// let shares = try await backend.getShares(byPath: "/\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await backend.assertDownloadLimit(by: token, count: 0, limit: 3)
-// }
-//
-// func testShareOfFolder() async throws {
-// let testSubject = "_Xcode UI Test Subject"
-// try await backend.createFolder(testSubject)
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testSubject)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap add share link button.
-//
-// let addShareLinkButton = app.buttons["addShareLink"]
-// guard addShareLinkButton.await() else { return }
-// addShareLinkButton.tap()
-//
-// // Verify download limits being unavailable.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// XCTAssertFalse(downloadLimitCell.exists, "Folder shares cannot have download limits")
-//
-// // Cleanup
-//
-// try await backend.delete(testSubject)
-// }
-//
-// func testAddingDownloadLimitToExistingShare() async throws {
-// try await backend.createShare(byPath: testFileName)
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap show share link details button.
-//
-// let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
-// guard showShareLinkDetailsButton.await() else { return }
-// showShareLinkDetailsButton.tap()
-//
-// // Tap share link details button in share menu sheet.
-//
-// let shareMenuDetailsCell = app.cells["shareMenu/details"]
-// guard shareMenuDetailsCell.await() else { return }
-// shareMenuDetailsCell.tap()
-//
-// // Tap download limits.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// guard downloadLimitCell.await() else { return }
-// downloadLimitCell.tap()
-//
-// // Tap download limit switch.
-//
-// let downloadLimitSwitch = app.switches["downloadLimitSwitch"]
-// guard downloadLimitSwitch.await() else { return }
-// downloadLimitSwitch.tap()
-//
-// // Update allowed downloads.
-//
-// let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
-// guard allowedDownloadsTextField.await() else { return }
-// allowedDownloadsTextField.tap()
-// allowedDownloadsTextField.typeText("3")
-//
-// // Tap navigation back button.
-//
-// app.navigationBars.buttons.firstMatch.tap()
-//
-// // Tap confirm share button.
-//
-// let confirmShareButton = app.buttons["confirmShare"]
-// guard confirmShareButton.await() else { return }
-// confirmShareButton.tap()
-// confirmShareButton.awaitInexistence()
-//
-// // Then
-// let shares = try await backend.getShares(byPath: "\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await aMoment()
-// try await backend.assertDownloadLimit(by: token, count: 0, limit: 3)
-// }
-//
-// func testUpdatingDownloadLimitOnExistingShare() async throws {
-// try await backend.createShare(byPath: testFileName)
-// var shares = try await backend.getShares(byPath: testFileName)
-//
-// guard let token = shares.first?.token else {
-// XCTFail("Failed to fetch token of share for test file!")
-// return
-// }
-//
-// try await backend.setDownloadLimit(to: 4, by: token)
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap show share link details button.
-//
-// let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
-// guard showShareLinkDetailsButton.await() else { return }
-// showShareLinkDetailsButton.tap()
-//
-// // Tap share link details button in share menu sheet.
-//
-// let shareMenuDetailsCell = app.cells["shareMenu/details"]
-// guard shareMenuDetailsCell.await() else { return }
-// shareMenuDetailsCell.tap()
-//
-// // Tap download limits.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// guard downloadLimitCell.await() else { return }
-// downloadLimitCell.tap()
-//
-// // Check download limit switch.
-//
-// let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
-// guard downloadLimitSwitch.await() else { return }
-//
-// guard let downloadLimitSwitchValue = (downloadLimitSwitch.value as? String) else {
-// XCTFail("Failed to get value of user interface control!")
-// return
-// }
-//
-// guard downloadLimitSwitchValue == "1" else {
-// XCTFail("The switch is not on!")
-// return
-// }
-//
-// // Check and update allowed downloads.
-//
-// let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
-// guard allowedDownloadsTextField.await() else { return }
-//
-// guard let allowedDownloadsTextFieldValue = (allowedDownloadsTextField.value as? String) else {
-// XCTFail("Failed to get value of user interface control!")
-// return
-// }
-//
-// guard allowedDownloadsTextFieldValue == "4" else {
-// XCTFail("The text field value is wrong!")
-// return
-// }
-//
-// allowedDownloadsTextField.tap()
-// allowedDownloadsTextField.typeText("6")
-//
-// // Tap navigation back button.
-//
-// app.navigationBars.buttons.firstMatch.tap()
-//
-// // Tap confirm share button.
-//
-// let confirmShareButton = app.buttons["confirmShare"]
-// guard confirmShareButton.await() else { return }
-// confirmShareButton.tap()
-// confirmShareButton.awaitInexistence()
-//
-// // Then
-// shares = try await backend.getShares(byPath: "\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await aMoment()
-// try await backend.assertDownloadLimit(by: token, count: 0, limit: 6)
-// }
-//
-// func testDiscardingDownloadLimitChangesOnExistingShare() async throws {
-// try await backend.createShare(byPath: testFileName)
-// var shares = try await backend.getShares(byPath: testFileName)
-//
-// guard let token = shares.first?.token else {
-// XCTFail("Failed to fetch token of share for test file!")
-// return
-// }
-//
-// try await backend.setDownloadLimit(to: 8, by: token)
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap show share link details button.
-//
-// let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
-// guard showShareLinkDetailsButton.await() else { return }
-// showShareLinkDetailsButton.tap()
-//
-// // Tap share link details button in share menu sheet.
-//
-// let shareMenuDetailsCell = app.cells["shareMenu/details"]
-// guard shareMenuDetailsCell.await() else { return }
-// shareMenuDetailsCell.tap()
-//
-// // Tap download limits.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// guard downloadLimitCell.await() else { return }
-// downloadLimitCell.tap()
-//
-// // Check download limit switch.
-//
-// let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
-// guard downloadLimitSwitch.await() else { return }
-//
-// guard let downloadLimitSwitchValue = (downloadLimitSwitch.value as? String) else {
-// XCTFail("Failed to get value of user interface control!")
-// return
-// }
-//
-// guard downloadLimitSwitchValue == "1" else {
-// XCTFail("The switch is not on!")
-// return
-// }
-//
-// // Check and update allowed downloads.
-//
-// let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
-// guard allowedDownloadsTextField.await() else { return }
-//
-// guard let allowedDownloadsTextFieldValue = (allowedDownloadsTextField.value as? String) else {
-// XCTFail("Failed to get value of user interface control!")
-// return
-// }
-//
-// allowedDownloadsTextField.tap()
-// allowedDownloadsTextField.typeText("9")
-//
-// // Tap navigation back button.
-//
-// app.navigationBars.buttons.firstMatch.tap()
-//
-// // Tap confirm share button.
-//
-// let cancelShareButton = app.buttons["cancelShare"]
-// guard cancelShareButton.await() else { return }
-// cancelShareButton.tap()
-//
-// // Then
-// shares = try await backend.getShares(byPath: "\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await aMoment()
-// try await backend.assertDownloadLimit(by: token, count: 0, limit: 8)
-// }
-//
-// func testRemovingDownloadLimitFromExistingShare() async throws {
-// try await backend.createShare(byPath: testFileName)
-// var shares = try await backend.getShares(byPath: testFileName)
-//
-// guard let token = shares.first?.token else {
-// XCTFail("Failed to fetch token of share for test file!")
-// return
-// }
-//
-// try await backend.setDownloadLimit(to: 4, by: token)
-// pullToRefresh()
-//
-// // Tap share button.
-//
-// let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
-// guard shareButton.await() else { return }
-// shareButton.tap()
-//
-// // Tap show share link details button.
-//
-// let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
-// guard showShareLinkDetailsButton.await() else { return }
-// showShareLinkDetailsButton.tap()
-//
-// // Tap share link details button in share menu sheet.
-//
-// let shareMenuDetailsCell = app.cells["shareMenu/details"]
-// guard shareMenuDetailsCell.await() else { return }
-// shareMenuDetailsCell.tap()
-//
-// // Tap download limits.
-//
-// let downloadLimitCell = app.cells["downloadLimit"]
-// guard downloadLimitCell.await() else { return }
-// downloadLimitCell.tap()
-//
-// // Check download limit switch.
-//
-// let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
-// guard downloadLimitSwitch.await() else { return }
-// downloadLimitSwitch.tap()
-//
-// // Tap navigation back button.
-//
-// app.navigationBars.buttons.firstMatch.tap()
-//
-// // Tap confirm share button.
-//
-// let confirmShareButton = app.buttons["confirmShare"]
-// guard confirmShareButton.await() else { return }
-// confirmShareButton.tap()
-// confirmShareButton.awaitInexistence()
-//
-// // Then
-// shares = try await backend.getShares(byPath: "\(testFileName)")
-// XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
-//
-// guard let token = shares.first?.token else {
-// throw UITestError.missingValue
-// }
-//
-// try await aMoment()
-// try await backend.assertDownloadLimit(by: token, count: nil, limit: nil)
-// }
-//}
+// SPDX-FileCopyrightText: Nextcloud GmbH
+// SPDX-FileCopyrightText: 2025 Iva Horn
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import XCTest
+
+///
+/// User interface tests for the download limits management on shares.
+///
+@MainActor
+final class DownloadLimitUITests: BaseUIXCTestCase {
+ ///
+ /// Name of the file to work with.
+ ///
+ /// The leading underscore is required for the file to appear at the top of the list.
+ /// Obviously, this is fragile by making some assumptions of the user interface state.
+ ///
+ let testFileName = "_Xcode UI Test Subject.md"
+
+ // MARK: - Lifecycle
+
+ override func setUp() async throws {
+ try await super.setUp()
+ continueAfterFailure = false
+
+ // Handle alerts presented by the system.
+ addUIInterruptionMonitor(withDescription: "Allow Notifications", for: "Allow")
+ addUIInterruptionMonitor(withDescription: "Save Password", for: "Not Now")
+
+ // Launch the app.
+ app = XCUIApplication()
+ app.launchArguments = ["UI_TESTING"]
+ app.launch()
+
+ try await logIn()
+
+ // Set up test backend communication.
+ backend = UITestBackend()
+
+ try await backend.assertCapability(true, capability: \.downloadLimit)
+ try await backend.delete(testFileName)
+ try await backend.prepareTestFile(testFileName)
+ }
+
+ // MARK: - Tests
+
+ func testShareWithoutDownloadLimitCapability() async throws {
+ // This cannot be implemented at the time of writing.
+ // There is no way to disable and enable server apps via web API.
+ // The Xcode UI test process cannot access Docker.
+ throw XCTSkip("Not implemented yet!")
+ }
+
+ func testNewShareWithoutDownloadLimit() async throws {
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+
+ guard shareButton.exists else {
+ throw UITestError.waitForExistence(shareButton)
+ }
+
+ shareButton.tap()
+
+ // Tap add share link button.
+
+ let addShareLinkButton = app.buttons["addShareLink"]
+ guard addShareLinkButton.await() else { return }
+ addShareLinkButton.tap()
+
+ // Tap confirm share button.
+
+ let confirmShareButton = app.buttons["confirmShare"]
+
+ guard confirmShareButton.exists else {
+ throw UITestError.waitForExistence(confirmShareButton)
+ }
+
+ confirmShareButton.tap()
+ confirmShareButton.awaitInexistence()
+
+ // Then
+ let shares = try await backend.getShares(byPath: "/\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await backend.assertNoDownloadLimit(by: token)
+ }
+
+ func testNewShareWithDownloadLimit() async throws {
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap add share link button.
+
+ let addShareLinkButton = app.buttons["addShareLink"]
+ guard addShareLinkButton.await() else { return }
+ addShareLinkButton.tap()
+
+ // Tap download limits.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ guard downloadLimitCell.await() else { return }
+ downloadLimitCell.tap()
+
+ // Tap download limit switch.
+
+ let downloadLimitSwitch = app.switches["downloadLimitSwitch"]
+ guard downloadLimitSwitch.await() else { return }
+ downloadLimitSwitch.tap()
+
+ // Update allowed downloads.
+
+ let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
+ guard allowedDownloadsTextField.await() else { return }
+ allowedDownloadsTextField.tap()
+ allowedDownloadsTextField.typeText("3")
+
+ // Tap navigation back button.
+
+ app.navigationBars.buttons.firstMatch.tap()
+
+ // Tap confirm share button.
+
+ let confirmShareButton = app.buttons["confirmShare"]
+ guard confirmShareButton.await() else { return }
+ confirmShareButton.tap()
+ confirmShareButton.awaitInexistence()
+
+ // Then
+ let shares = try await backend.getShares(byPath: "/\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await backend.assertDownloadLimit(by: token, count: 0, limit: 3)
+ }
+
+ func testShareOfFolder() async throws {
+ let testSubject = "_Xcode UI Test Subject"
+ try await backend.createFolder(testSubject)
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testSubject)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap add share link button.
+
+ let addShareLinkButton = app.buttons["addShareLink"]
+ guard addShareLinkButton.await() else { return }
+ addShareLinkButton.tap()
+
+ // Verify download limits being unavailable.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ XCTAssertFalse(downloadLimitCell.exists, "Folder shares cannot have download limits")
+
+ // Cleanup
+
+ try await backend.delete(testSubject)
+ }
+
+ func testAddingDownloadLimitToExistingShare() async throws {
+ try await backend.createShare(byPath: testFileName)
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap show share link details button.
+
+ let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
+ guard showShareLinkDetailsButton.await() else { return }
+ showShareLinkDetailsButton.tap()
+
+ // Tap share link details button in share menu sheet.
+
+ let shareMenuDetailsCell = app.cells["shareMenu/details"]
+ guard shareMenuDetailsCell.await() else { return }
+ shareMenuDetailsCell.tap()
+
+ // Tap download limits.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ guard downloadLimitCell.await() else { return }
+ downloadLimitCell.tap()
+
+ // Tap download limit switch.
+
+ let downloadLimitSwitch = app.switches["downloadLimitSwitch"]
+ guard downloadLimitSwitch.await() else { return }
+ downloadLimitSwitch.tap()
+
+ // Update allowed downloads.
+
+ let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
+ guard allowedDownloadsTextField.await() else { return }
+ allowedDownloadsTextField.tap()
+ allowedDownloadsTextField.typeText("3")
+
+ // Tap navigation back button.
+
+ app.navigationBars.buttons.firstMatch.tap()
+
+ // Tap confirm share button.
+
+ let confirmShareButton = app.buttons["confirmShare"]
+ guard confirmShareButton.await() else { return }
+ confirmShareButton.tap()
+ confirmShareButton.awaitInexistence()
+
+ // Then
+ let shares = try await backend.getShares(byPath: "\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await aMoment()
+ try await backend.assertDownloadLimit(by: token, count: 0, limit: 3)
+ }
+
+ func testUpdatingDownloadLimitOnExistingShare() async throws {
+ try await backend.createShare(byPath: testFileName)
+ var shares = try await backend.getShares(byPath: testFileName)
+
+ guard let token = shares.first?.token else {
+ XCTFail("Failed to fetch token of share for test file!")
+ return
+ }
+
+ try await backend.setDownloadLimit(to: 4, by: token)
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap show share link details button.
+
+ let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
+ guard showShareLinkDetailsButton.await() else { return }
+ showShareLinkDetailsButton.tap()
+
+ // Tap share link details button in share menu sheet.
+
+ let shareMenuDetailsCell = app.cells["shareMenu/details"]
+ guard shareMenuDetailsCell.await() else { return }
+ shareMenuDetailsCell.tap()
+
+ // Tap download limits.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ guard downloadLimitCell.await() else { return }
+ downloadLimitCell.tap()
+
+ // Check download limit switch.
+
+ let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
+ guard downloadLimitSwitch.await() else { return }
+
+ guard let downloadLimitSwitchValue = (downloadLimitSwitch.value as? String) else {
+ XCTFail("Failed to get value of user interface control!")
+ return
+ }
+
+ guard downloadLimitSwitchValue == "1" else {
+ XCTFail("The switch is not on!")
+ return
+ }
+
+ // Check and update allowed downloads.
+
+ let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
+ guard allowedDownloadsTextField.await() else { return }
+
+ guard let allowedDownloadsTextFieldValue = (allowedDownloadsTextField.value as? String) else {
+ XCTFail("Failed to get value of user interface control!")
+ return
+ }
+
+ guard allowedDownloadsTextFieldValue == "4" else {
+ XCTFail("The text field value is wrong!")
+ return
+ }
+
+ allowedDownloadsTextField.tap()
+ allowedDownloadsTextField.typeText("6")
+
+ // Tap navigation back button.
+
+ app.navigationBars.buttons.firstMatch.tap()
+
+ // Tap confirm share button.
+
+ let confirmShareButton = app.buttons["confirmShare"]
+ guard confirmShareButton.await() else { return }
+ confirmShareButton.tap()
+ confirmShareButton.awaitInexistence()
+
+ // Then
+ shares = try await backend.getShares(byPath: "\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await aMoment()
+ try await backend.assertDownloadLimit(by: token, count: 0, limit: 6)
+ }
+
+ func testDiscardingDownloadLimitChangesOnExistingShare() async throws {
+ try await backend.createShare(byPath: testFileName)
+ var shares = try await backend.getShares(byPath: testFileName)
+
+ guard let token = shares.first?.token else {
+ XCTFail("Failed to fetch token of share for test file!")
+ return
+ }
+
+ try await backend.setDownloadLimit(to: 8, by: token)
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap show share link details button.
+
+ let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
+ guard showShareLinkDetailsButton.await() else { return }
+ showShareLinkDetailsButton.tap()
+
+ // Tap share link details button in share menu sheet.
+
+ let shareMenuDetailsCell = app.cells["shareMenu/details"]
+ guard shareMenuDetailsCell.await() else { return }
+ shareMenuDetailsCell.tap()
+
+ // Tap download limits.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ guard downloadLimitCell.await() else { return }
+ downloadLimitCell.tap()
+
+ // Check download limit switch.
+
+ let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
+ guard downloadLimitSwitch.await() else { return }
+
+ guard let downloadLimitSwitchValue = (downloadLimitSwitch.value as? String) else {
+ XCTFail("Failed to get value of user interface control!")
+ return
+ }
+
+ guard downloadLimitSwitchValue == "1" else {
+ XCTFail("The switch is not on!")
+ return
+ }
+
+ // Check and update allowed downloads.
+
+ let allowedDownloadsTextField = app.textFields["downloadLimitTextField"]
+ guard allowedDownloadsTextField.await() else { return }
+
+ guard let allowedDownloadsTextFieldValue = (allowedDownloadsTextField.value as? String) else {
+ XCTFail("Failed to get value of user interface control!")
+ return
+ }
+
+ allowedDownloadsTextField.tap()
+ allowedDownloadsTextField.typeText("9")
+
+ // Tap navigation back button.
+
+ app.navigationBars.buttons.firstMatch.tap()
+
+ // Tap confirm share button.
+
+ let cancelShareButton = app.buttons["cancelShare"]
+ guard cancelShareButton.await() else { return }
+ cancelShareButton.tap()
+
+ // Then
+ shares = try await backend.getShares(byPath: "\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await aMoment()
+ try await backend.assertDownloadLimit(by: token, count: 0, limit: 8)
+ }
+
+ func testRemovingDownloadLimitFromExistingShare() async throws {
+ try await backend.createShare(byPath: testFileName)
+ var shares = try await backend.getShares(byPath: testFileName)
+
+ guard let token = shares.first?.token else {
+ XCTFail("Failed to fetch token of share for test file!")
+ return
+ }
+
+ try await backend.setDownloadLimit(to: 4, by: token)
+ pullToRefresh()
+
+ // Tap share button.
+
+ let shareButton = app.buttons["Cell/\(testFileName)/shareButton"]
+ guard shareButton.await() else { return }
+ shareButton.tap()
+
+ // Tap show share link details button.
+
+ let showShareLinkDetailsButton = app.buttons["showShareLinkDetails"]
+ guard showShareLinkDetailsButton.await() else { return }
+ showShareLinkDetailsButton.tap()
+
+ // Tap share link details button in share menu sheet.
+
+ let shareMenuDetailsCell = app.cells["shareMenu/details"]
+ guard shareMenuDetailsCell.await() else { return }
+ shareMenuDetailsCell.tap()
+
+ // Tap download limits.
+
+ let downloadLimitCell = app.cells["downloadLimit"]
+ guard downloadLimitCell.await() else { return }
+ downloadLimitCell.tap()
+
+ // Check download limit switch.
+
+ let downloadLimitSwitch = app.switches["downloadLimitSwitch"].firstMatch
+ guard downloadLimitSwitch.await() else { return }
+ downloadLimitSwitch.tap()
+
+ // Tap navigation back button.
+
+ app.navigationBars.buttons.firstMatch.tap()
+
+ // Tap confirm share button.
+
+ let confirmShareButton = app.buttons["confirmShare"]
+ guard confirmShareButton.await() else { return }
+ confirmShareButton.tap()
+ confirmShareButton.awaitInexistence()
+
+ // Then
+ shares = try await backend.getShares(byPath: "\(testFileName)")
+ XCTAssertEqual(shares.count, 1, "Only one share existing on \(testFileName)")
+
+ guard let token = shares.first?.token else {
+ throw UITestError.missingValue
+ }
+
+ try await aMoment()
+ try await backend.assertDownloadLimit(by: token, count: nil, limit: nil)
+ }
+}
diff --git a/Tests/NextcloudUnitTests/AssetUploadTest.swift b/Tests/NextcloudUnitTests/AssetUploadTest.swift
new file mode 100644
index 0000000000..72f541042b
--- /dev/null
+++ b/Tests/NextcloudUnitTests/AssetUploadTest.swift
@@ -0,0 +1,36 @@
+//
+// AssetUploadTest.swift
+// NextcloudTests
+//
+// Created by A200020526 on 12/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+
+final class AssetUploadTest: XCTestCase {
+
+ 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 testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/Tests/NextcloudUnitTests/AudioUploadTests.swift b/Tests/NextcloudUnitTests/AudioUploadTests.swift
new file mode 100644
index 0000000000..2ea01a8085
--- /dev/null
+++ b/Tests/NextcloudUnitTests/AudioUploadTests.swift
@@ -0,0 +1,52 @@
+//
+// AudioUploadTests.swift
+// NextcloudTests
+//
+// Created by A200020526 on 13/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+@testable import Nextcloud
+
+final class AudioUploadTests: XCTestCase {
+ var viewController:NCAudioRecorderViewController?
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ // Step 1. Create an instance of UIStoryboard
+ let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController
+ // Step 3. Make the viewDidLoad() execute.
+ viewController?.loadViewIfNeeded()
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ viewController = nil
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+ func testAudioMeterUpdateAfterDb(){
+ viewController?.audioMeterDidUpdate(0.5)
+ XCTAssertNotNil(!(viewController?.durationLabel.text?.isEmpty ?? false))
+ }
+
+ func testStartRecorder(){
+ viewController?.startStop()
+ XCTAssertEqual(viewController?.recording.state, nil, "Test start audio recorder")
+ }
+}
diff --git a/Tests/NextcloudUnitTests/CollaboraTestCase.swift b/Tests/NextcloudUnitTests/CollaboraTestCase.swift
new file mode 100644
index 0000000000..51b3ecce1f
--- /dev/null
+++ b/Tests/NextcloudUnitTests/CollaboraTestCase.swift
@@ -0,0 +1,142 @@
+//
+// CollaboraTestCase.swift
+// NextcloudTests
+//
+// Created by A200073704 on 06/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class CollaboraTestCase: XCTestCase {
+
+
+ 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 testCollaboraDocumentIsPresent() {
+
+ var viewForDocument: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_document") {
+ viewForDocument = NCMenuAction(title: NSLocalizedString("_create_new_document_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_document_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForDocument)
+
+ }
+
+ func testCollaboraPresentationIsPresent() {
+
+ var viewForPresentation: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_ppt") {
+ viewForPresentation = NCMenuAction(title: NSLocalizedString("_create_new_presentation_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForPresentation)
+
+ }
+
+ func testCollaboraSpreadsheetIsPresent() {
+
+ var viewForSpreadsheet: NCMenuAction?
+
+ if let image = UIImage(named: "create_file_xls") {
+ viewForSpreadsheet = NCMenuAction(title: NSLocalizedString("_create_new_spreadsheet_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(viewForSpreadsheet)
+
+ }
+
+ func testTextDocumentIsPresent() {
+
+ var textMenu: NCMenuAction?
+
+ if let image = UIImage(named: "file_txt_menu") {
+ textMenu = NCMenuAction(title: NSLocalizedString("_create_nextcloudtext_document_", comment: ""), icon: image, action: { _ in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController() as? UINavigationController else {
+ return
+ }
+
+ let viewController = navigationController.topViewController as? NCCreateFormUploadDocuments
+ viewController?.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
+ })
+ }
+
+ XCTAssertNotNil(textMenu)
+
+ }
+
+ func testTextDocumentAction() {
+
+ let text = NCGlobal.shared.actionTextDocument
+ XCTAssertNotNil(text, "Text Editor Should be opened")
+ }
+
+ func testTextFieldIsPresent() {
+
+ let storyboard = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCCreateFormUploadDocuments else {
+ return
+ }
+
+ // Verify that a text field is present in the view controller
+ let textFields = viewController.view.subviews.filter { $0 is UITextField }
+ XCTAssertFalse(textFields.isEmpty, "No text field found in NCCreateFormUploadDocuments")
+ }
+
+ func testSavePathFolder() {
+
+ let viewController = NCCreateFormUploadDocuments()
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var row : XLFormRowDescriptor
+
+ // the section with the title "Folder Destination"
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
+ row.action.formSelector = #selector(viewController.changeDestinationFolder(_:))
+
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected save path section to exist in form.")
+
+ }
+
+
+
+
+
+
+}
diff --git a/Tests/NextcloudUnitTests/E2EETests.swift b/Tests/NextcloudUnitTests/E2EETests.swift
new file mode 100644
index 0000000000..bee1c1aa3f
--- /dev/null
+++ b/Tests/NextcloudUnitTests/E2EETests.swift
@@ -0,0 +1,90 @@
+//
+// E2EETests.swift
+// NextcloudTests
+//
+// Created by A200020526 on 26/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import TOPasscodeViewController
+
+final class E2EETests: XCTestCase {
+
+ var manageE2EE: NCManageE2EE!
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ manageE2EE = NCManageE2EE()
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ manageE2EE = nil
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+ // MARK: - Initialization
+
+ func testInitialization() {
+ XCTAssertTrue(manageE2EE.endToEndInitialize.delegate === manageE2EE)
+ XCTAssertFalse(manageE2EE.isEndToEndEnabled)
+ XCTAssertEqual(manageE2EE.statusOfService, NSLocalizedString("_status_in_progress_", comment: ""))
+ }
+
+ // MARK: - Delegate
+
+ func testEndToEndInitializeSuccess() {
+ manageE2EE.endToEndInitializeSuccess()
+ XCTAssertTrue(manageE2EE.isEndToEndEnabled)
+ }
+
+ // MARK: - Passcode
+
+ func testRequestPasscodeType() {
+ // TODO: Implement this test case
+ }
+
+ func testCorrectPasscode_startE2E() {
+ manageE2EE.passcodeType = "startE2E"
+ manageE2EE.correctPasscode()
+ // TODO: Add assertions for the expected behavior after entering the correct passcode for starting E2E
+ }
+
+ func testCorrectPasscode_readPassphrase() {
+ manageE2EE.passcodeType = "readPassphrase"
+ // TODO: Simulate entering the correct passcode and verify the expected behavior
+ }
+
+ func testCorrectPasscode_removeLocallyEncryption() {
+ manageE2EE.passcodeType = "removeLocallyEncryption"
+ // TODO: Simulate entering the correct passcode and verify the expected behavior
+ }
+
+ func testDidPerformBiometricValidationRequest() {
+ let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: true)
+ manageE2EE.didPerformBiometricValidationRequest(in: passcodeViewController)
+ // TODO: Add assertions for the expected behavior after performing biometric validation
+ }
+
+ func testDidTapCancel() {
+ let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: true)
+ manageE2EE.didTapCancel(in: passcodeViewController)
+ // TODO: Add assertions for the expected behavior after tapping cancel
+ }
+}
diff --git a/Tests/NextcloudUnitTests/MoreTests.swift b/Tests/NextcloudUnitTests/MoreTests.swift
new file mode 100644
index 0000000000..0a96912994
--- /dev/null
+++ b/Tests/NextcloudUnitTests/MoreTests.swift
@@ -0,0 +1,37 @@
+//
+// MoreTests.swift
+// NextcloudTests
+//
+// Created by A200020526 on 09/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+
+final class MoreTests: XCTestCase {
+
+ 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 testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/Tests/NextcloudUnitTests/MoveAndCopyTests.swift b/Tests/NextcloudUnitTests/MoveAndCopyTests.swift
new file mode 100644
index 0000000000..bd674a472f
--- /dev/null
+++ b/Tests/NextcloudUnitTests/MoveAndCopyTests.swift
@@ -0,0 +1,106 @@
+//
+// MoveAndCopyTests.swift
+// NextcloudTests
+//
+// Created by A200073704 on 05/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+
+ class MoveAndCopyTests: XCTestCase {
+
+ var view : NCSelectCommandView?
+ var viewController : NCSelect?
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController {
+ let viewController = navigationController.topViewController as? NCSelect
+
+ let _ = viewController?.view
+ viewController?.loadViewIfNeeded()
+ }
+ view = NCSelectCommandView()
+
+ }
+
+ override func setUp() {
+ super.setUp()
+ let nib = Bundle.main.loadNibNamed("NCSelectCommandViewCopyMove", owner: nil, options: nil)
+ view = nib?.first as? NCSelectCommandView
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+
+ viewController = nil
+ view = nil
+
+ }
+
+ func testCreateFolderButton() {
+
+ let image: Void? = view?.createFolderButton?.setImage(UIImage(named: "addFolder")?.withTintColor(UIColor.label), for: .normal)
+
+ XCTAssertNotNil(image)
+ }
+
+ func testOverwriteSwitch() {
+
+ let mySwitch = view?.overwriteSwitch
+
+ XCTAssertNotNil(mySwitch)
+
+ }
+
+ func testCopyButton() {
+
+ let copy = view?.copyButton
+
+ XCTAssertNotNil(copy)
+ }
+
+
+ func testOverwriteSwitchAlwaysOn() {
+
+ XCTAssertTrue(view?.overwriteSwitch?.isOn ?? false, "Overwrite Switch should always be on")
+ }
+
+ func testCopyButtonandMoveButtonCondition() {
+
+ // Disable copy and move
+ view?.copyButton?.isEnabled = false
+ view?.moveButton?.isEnabled = false
+
+ // Creating a test item
+ let item = tableMetadata()
+ item.serverUrl = "serverUrl" // Set the serverUrl property of the item
+
+ let items: [tableMetadata] = [item]
+
+ // Update the items in the view controller
+ viewController?.items = items
+
+ // Verify that the copy and move buttons are still disabled
+ XCTAssertFalse(view?.copyButton?.isEnabled ?? true, "Copy Button should remain disabled when items.first matches the condition")
+ XCTAssertFalse(view?.moveButton?.isEnabled ?? true, "Move Button should remain disabled when items.first matches the condition")
+
+ // Enable copy and move
+ view?.copyButton?.isEnabled = true
+ view?.moveButton?.isEnabled = true
+
+ // Update the items in the view controller
+ viewController?.items = [] // Empty items
+
+ // Verify that the copyButton is still enabled
+ XCTAssertTrue(view?.copyButton?.isEnabled ?? false, "Copy Button should remain enabled when items.first doesn't match the condition")
+ XCTAssertTrue(view?.moveButton?.isEnabled ?? false, "Move Button should remain enabled when items.first doesn't match the condition")
+ }
+
+}
diff --git a/Tests/NextcloudUnitTests/NCNotificationText.swift b/Tests/NextcloudUnitTests/NCNotificationText.swift
new file mode 100644
index 0000000000..1a7e0b2345
--- /dev/null
+++ b/Tests/NextcloudUnitTests/NCNotificationText.swift
@@ -0,0 +1,85 @@
+//
+// NCNotificationText.swift
+// NextcloudUnitTests
+//
+// Created by Amrut Waghmare on 18/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class NCNotificationText: XCTestCase {
+ var viewController : NCNotification!
+
+ override func setUpWithError() throws {
+ // Step 1. Create an instance of UIStoryboard
+ let storyboard = UIStoryboard(name: "NCNotification", bundle: nil)
+ // Step 2. Instantiate UIViewController with Storyboard ID
+ viewController = storyboard.instantiateViewController(withIdentifier: "NCNotification.storyboard") as? NCNotification
+
+ // Step 3. Make the viewDidLoad() execute.
+ viewController.loadViewIfNeeded()
+ }
+
+ override func tearDownWithError() throws {
+ viewController = nil
+ }
+
+ //Test that a cell with the correct reuse identifier is dequeued
+ func testTableViewCellDequeue() {
+ let notification = NKNotifications()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertNotNil(cell)
+ XCTAssertEqual(cell?.reuseIdentifier, "Cell")
+ }
+
+ //Test that the cell's icon is set image
+ func testTableViewCellIcon() {
+ let notification = NKNotifications()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertNotNil(cell?.icon.image)
+ }
+
+ //Test that the cell's primary and secondary buttons are set up correctly
+ func testTableViewCellButtons() {
+ let notification = NKNotifications()
+ notification.actions = Data("[{\"label\":\"OK\",\"primary\":true},{\"label\":\"Cancel\",\"primary\":false}]".utf8)
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertEqual(cell?.primary.title(for: .normal), "OK")
+ XCTAssertEqual(cell?.secondary.title(for: .normal), "Cancel")
+ }
+
+ //Test that the cell's date label is set correctly
+ func testTableViewCellDate() {
+ let notification = NKNotifications()
+ notification.date = NSDate()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertEqual(cell?.date.text, "less than a minute ago")
+ }
+
+ //Test with a color that is image not nil
+ func testImageNotNil() {
+ let color = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
+ let image = UIImage().imageColor(color)
+ XCTAssertNotNil(image, "Image should not be nil.")
+
+ }
+}
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/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
new file mode 100644
index 0000000000..f6265f48e7
--- /dev/null
+++ b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
@@ -0,0 +1,154 @@
+//
+// PrivacyPolicyTest.swift
+// NextcloudTests
+//
+// Created by A200073704 on 27/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+import XLForm
+
+
+ class PrivacyPolicyTest: XCTestCase {
+
+ var viewController: InitialPrivacySettingsViewController?
+ var privacySettingsView = PrivacySettingsViewController()
+
+ override func setUpWithError() throws {
+
+ // To Create an instance of UIStoryboard
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+
+ // To Instantiate UIViewController with Storyboard ID
+ viewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController
+
+ // Outlets are connected
+ let _ = viewController?.view
+
+ // Make the viewDidLoad() execute.
+ viewController?.loadViewIfNeeded()
+
+ }
+
+ override func tearDownWithError() throws {
+ viewController = nil
+ }
+
+ func testPrivacyPolicyViewControllerIsOpen() {
+
+ // Check that the InitialPrivacyPolicyViewController gets opened
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ if let privacyPolicyViewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController {
+ let navigationController = UINavigationController(rootViewController: privacyPolicyViewController)
+
+ privacyPolicyViewController.loadViewIfNeeded()
+
+ XCTAssertTrue(navigationController.topViewController is InitialPrivacySettingsViewController, "Privacy policy view controller should be open")
+ }
+ }
+
+ func testTextViewHasCorrectText() {
+
+ //Check that the text displayed is correct
+ let expectedText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ viewController?.privacySettingsHelpText?.text = expectedText
+
+ let actualText = viewController?.privacySettingsHelpText?.text
+ XCTAssertEqual(actualText, expectedText, "The text view does not have the expected text")
+ }
+
+ func testHasAcceptButton() {
+
+ // Check that view has the accept button
+ let acceptButton = viewController?.acceptButton
+
+ XCTAssertNotNil(acceptButton, "View controller does not have an accept button")
+
+ }
+
+ func testSettingsLinkTypeNavigatesToPrivacySettingsViewController() {
+
+ // Simulate tapping the "Settings" link type
+ let linkType = LinkType.settings
+
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_settings_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation stack
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacySettingsViewController)
+ }
+
+ func testPrivacyPolicyLinkType_NavigatesToPrivacyPolicyViewController() {
+
+ // Simulate tapping the "Privacy Policy" link type
+ let linkType = LinkType.privacyPolicy
+
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacyPolicyViewController)
+ }
+
+ func testCorrectImagePresentOnInitialPrivacySettingsViewController() {
+
+ // Check that the image view has the correct image
+ let expectedImage = UIImage(named: "dataPrivacy")
+ XCTAssertNotNil(expectedImage)
+ }
+
+ func testAcceptButtonHasBackgroundColor() {
+
+ // Check that the accept button has the correct background color
+ let expectedColor = NCBrandColor.shared.brand
+ XCTAssertEqual(viewController?.acceptButton.backgroundColor, expectedColor)
+
+ }
+
+ func testShowSaveSettingsButton() {
+
+ privacySettingsView.isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+
+ XCTAssertTrue(privacySettingsView.isShowSettingsButton)
+
+ }
+
+ func testRequiredDataCollectionSectionExists() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // the section with the title "Required Data Collection"
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected 'Required Data Collection' section to exist in form.")
+ }
+
+ func testAnalysisDataCollectionSection() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // row with tag "AnalysisDataCollectionSwitch"
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+
+ // Assert that the row exists
+ XCTAssertNotNil(row, "Expected row with tag 'AnalysisDataCollectionSwitch' to exist in form.")
+
+ // Verify the switch is off
+ XCTAssertFalse(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn"), "Expected isAnalysisDataCollectionSwitchOn to be false.")
+ }
+
+
+}
diff --git a/Tests/NextcloudUnitTests/RenameFileTests.swift b/Tests/NextcloudUnitTests/RenameFileTests.swift
new file mode 100644
index 0000000000..c0aa06f029
--- /dev/null
+++ b/Tests/NextcloudUnitTests/RenameFileTests.swift
@@ -0,0 +1,108 @@
+//
+// RenameFileTests.swift
+// NextcloudTests
+//
+// Created by A200073704 on 14/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class RenameFileTests: XCTestCase {
+
+
+ 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 testStoryboardPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ XCTAssertNotNil(storyboard, "Storyboard 'NCRenameFile' should be present")
+
+ }
+
+ func testRenameButtonPresence() {
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let renameButton = viewController.renameButton
+ XCTAssertNotNil(renameButton, "Rename button should be present")
+ }
+
+ func testRenameButtonBackgroundColor() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let color = NCBrandColor.shared.brand.cgColor
+ let renameButton = viewController.renameButton.layer.backgroundColor
+
+ XCTAssertEqual(renameButton,color, "Rename Button Bcakground Color should be brand")
+ }
+
+ func testCancelButtonPresence() {
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let cancelButton = viewController.cancelButton
+ XCTAssertNotNil(cancelButton, "Cancel button should be present")
+ }
+
+ func testImageViewPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let imageView = viewController.previewFile
+ XCTAssertNotNil(imageView, "UIImageView should be present on the storyboard")
+ }
+
+ func testTextFiledPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let textField = viewController.fileNameNoExtension
+ let textFieldExt = viewController.ext
+
+ XCTAssertNotNil(textField, "FileNameNoExtention TextFiled should be present on the storyboard")
+ XCTAssertNotNil(textFieldExt, "Extension TextFiled should be present on the storyboard")
+
+ }
+
+
+
+}
diff --git a/Tests/NextcloudUnitTests/ScanTests.swift b/Tests/NextcloudUnitTests/ScanTests.swift
new file mode 100644
index 0000000000..750fc86d01
--- /dev/null
+++ b/Tests/NextcloudUnitTests/ScanTests.swift
@@ -0,0 +1,111 @@
+//
+// ScanTests.swift
+// NextcloudTests
+//
+// Created by A200020526 on 18/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import XLForm
+
+final class ScanTests: XCTestCase {
+
+ var viewController: NCCreateFormUploadScanDocument?
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ viewController = NCCreateFormUploadScanDocument()
+ }
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ viewController = nil
+ }
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+ public func testImageColor() {
+ // Create a test color
+ let testColor = UIColor.red
+ // Create a test image
+ let testImage = UIImage(named: "activityTypeInfo") // Replace "your_image_name" with the name of your test image
+ // Call the imageColor function with the test color
+ let resultImage = testImage?.imageColor(testColor)
+ // Assert that the result image is not nil
+ XCTAssertNotNil(resultImage, "Result image should not be nil")
+ // Assert that the result image has the same size as the test image
+ XCTAssertEqual(resultImage?.size, testImage?.size, "Result image should have the same size as the test image")
+ }
+ func testIsAtleastOneFiletypeSelected() {
+ // Set up the initial switch states
+ viewController?.isPDFWithOCRSwitchOn = false
+ viewController?.isPDFWithoutOCRSwitchOn = false
+ viewController?.isTextFileSwitchOn = false
+ viewController?.isPNGFormatSwitchOn = false
+ viewController?.isJPGFormatSwitchOn = false
+
+ // Call the function under test
+ let result1 = viewController?.isAtleastOneFiletypeSelected() ?? false
+
+ // Assert the initial result
+ XCTAssertFalse(result1, "None of the file types are selected initially")
+
+ // Update switch states
+ viewController?.isPDFWithOCRSwitchOn = true
+
+ // Call the function under test again
+ let result2 = viewController?.isAtleastOneFiletypeSelected() ?? false
+
+ // Assert the updated result
+ XCTAssertTrue(result2, "At least one file type is selected")
+ }
+
+ func testBestFittingFont() {
+ // Set up the initial values
+ let text = "Hello, World!"
+ let bounds = CGRect(x: 0, y: 0, width: 200, height: 100)
+ let fontDescriptor = UIFontDescriptor(name: "Helvetica", size: 20)
+ let fontColor = UIColor.black
+
+ // Call the function under test
+ let attributes = viewController?.bestFittingFont(for: text, in: bounds, fontDescriptor: fontDescriptor, fontColor: fontColor)
+
+ // Assert the results
+ XCTAssertNotNil(attributes?[NSAttributedString.Key.font], "Font attribute should not be nil")
+ XCTAssertNotNil(attributes?[NSAttributedString.Key.foregroundColor], "Font color attribute should not be nil")
+ XCTAssertNotNil(attributes?[NSAttributedString.Key.kern], "Kern attribute should not be nil")
+
+ XCTAssertEqual(attributes?[NSAttributedString.Key.foregroundColor] as? UIColor, fontColor, "Font color should match the input value")
+
+ let font = attributes?[NSAttributedString.Key.font] as? UIFont
+ XCTAssertNotNil(font, "Font should not be nil")
+ XCTAssertEqual(font?.fontName, fontDescriptor.fontAttributes[.name] as? String, "Font name should match the input value")
+ }
+
+ func testChangeCompressionImage() {
+ // Set up the initial values
+ guard let image = UIImage(named: "activityTypeInfo") else {
+ return
+ }
+
+ // Call the function under test
+ let compressedImage = viewController?.changeCompressionImage(image)
+
+ // Assert the results
+ XCTAssertNotNil(compressedImage, "Compressed image should not be nil")
+ if let width = compressedImage?.size.width, let height = compressedImage?.size.height {
+ XCTAssertTrue(width <= 841.8, "Compressed image width should be less than or equal to the base width")
+ XCTAssertTrue(height <= 595.2, "Compressed image height should be less than or equal to the base height")
+ }
+ }
+}
diff --git a/Tests/NextcloudUnitTests/SettingsTestCases.swift b/Tests/NextcloudUnitTests/SettingsTestCases.swift
new file mode 100644
index 0000000000..13c4dacb82
--- /dev/null
+++ b/Tests/NextcloudUnitTests/SettingsTestCases.swift
@@ -0,0 +1,341 @@
+//
+// SettingsTestCases.swift
+// NextcloudTests
+//
+// Created by A200073704 on 12/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+import XLForm
+
+ class SettingsTestCases: XCTestCase {
+
+
+ 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.
+ }
+
+ // MARK: - Settings
+
+ func testAutoUploadSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ let image = UIImage(named: "autoUpload")
+ row = XLFormRowDescriptor(tag: "autoUpload", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_settings_autoupload_", comment: ""));
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.addFormRow(row)
+
+ XCTAssertNotNil(image)
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected 'Auto Upload' section to exist in form.")
+
+
+ }
+
+ func testLockSectionIsPresent() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ let image = UIImage(named: "lock_open")
+ row = XLFormRowDescriptor(tag: "bloccopasscode", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_lock_not_active_", comment: ""));
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.addFormRow(row)
+
+ XCTAssertNotNil(image)
+
+ XCTAssertNotNil(row, "Expected 'Lock Active / Off ' section exists")
+
+
+ }
+
+ func testEnableTouchIDSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ row = XLFormRowDescriptor(tag: "enableTouchDaceID", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_enable_touch_face_id_", comment: ""));
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Enable/Disable touch ID' is present")
+
+ }
+
+ func testEndToEndEncryptionSectionIsPresent() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ row = XLFormRowDescriptor(tag: "e2eEncryption", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_e2e_settings_", comment: ""));
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'End to End encryption' section exists")
+
+
+ }
+
+ func testAdvancedSectionIsPresent() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ row = XLFormRowDescriptor(tag: "advanced", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_e2e_settings_", comment: ""));
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, " Expected 'Advanced' Section exists")
+ }
+
+
+
+ func testNavigatesToOpenSourceViewController() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "buttonLeftAligned", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_used_opensource_software_", comment: ""))
+ section.addFormRow(row)
+
+ // Create a view controller with the form and add it to a navigation controller
+ let viewController = OpenSourceSoftwareViewController()
+ let window = UIApplication.shared.windows.first { $0.isKeyWindow }
+ let navigationController = UINavigationController(rootViewController: viewController)
+ window?.rootViewController = navigationController
+
+ viewController.loadViewIfNeeded()
+ let indexPath = IndexPath(row: 0, section: 0)
+ print("Calling didSelectRowAt for row at \(indexPath)")
+
+ // Verify that the OpenSourceSoftwareViewController class is opened
+ XCTAssertTrue(navigationController.topViewController is OpenSourceSoftwareViewController)
+
+ }
+
+ func testHelpSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "buttonLeftAligned", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_help_", comment: ""))
+ section.addFormRow(row)
+
+ // Create a view controller with the form and add it to a navigation controller
+ let viewController = HelpViewController()
+ let window = UIApplication.shared.windows.first { $0.isKeyWindow }
+ let navigationController = UINavigationController(rootViewController: viewController)
+ window?.rootViewController = navigationController
+
+ viewController.loadViewIfNeeded()
+ let indexPath = IndexPath(row: 0, section: 0)
+ print("Calling didSelectRowAt for row at \(indexPath)")
+
+ // Verify that the HelpViewController class is opened
+ XCTAssertTrue(navigationController.topViewController is HelpViewController)
+
+ XCTAssertNotNil(row, "Expected 'Help' Section is present")
+
+ }
+
+ func testImprintSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "buttonLeftAligned", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_imprint_", comment: ""))
+ section.addFormRow(row)
+
+ // Create a view controller with the form and add it to a navigation controller
+ let viewController = ImprintViewController()
+ let window = UIApplication.shared.windows.first { $0.isKeyWindow }
+ let navigationController = UINavigationController(rootViewController: viewController)
+ window?.rootViewController = navigationController
+
+ viewController.loadViewIfNeeded()
+ let indexPath = IndexPath(row: 0, section: 0)
+ print("Calling didSelectRowAt for row at \(indexPath)")
+
+ // Verify that the ImprintViewController class is opened
+ XCTAssertTrue(navigationController.topViewController is ImprintViewController)
+
+ XCTAssertNotNil(row, "Expected 'Imprint' Section is present")
+ }
+
+ func testMagentaCloudVersionSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "disablefilesapp", rowType: "kNMCCustomCellType", title: NSLocalizedString("_magentacloud_version_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'MagentaCloud Version' is present")
+
+
+ }
+
+ // MARK: - Advanced
+
+ func testShowHiddenFilesSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "showHiddenFiles", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_show_hidden_files_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Show hidden files' section is present")
+
+ }
+
+ func testMostCompatibleSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "formatCompatibility", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_format_compatibility_", comment: ""))
+ row.value = "1"
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Most Compatible' is present")
+ }
+
+ func testLivePhotoSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "livePhoto", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_upload_mov_livephoto_", comment: ""))
+ if CCUtility.getLivePhoto() {
+ row.value = "1"
+ } else {
+ row.value = "0"
+ }
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Live Photo' section is present")
+
+ }
+
+ func testImageResolutionSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "automaticDownloadImage", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_automatic_Download_Image_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Use images in full resolution' section is present")
+ }
+
+ func testAppIntegrationSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "disablefilesapp", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_disable_files_app_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Disable Files App Integration' section is present")
+ }
+
+ func testDeleteFilesSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let row = XLFormRowDescriptor(tag: "deleteoldfiles", rowType: XLFormRowDescriptorTypeSelectorPush, title: NSLocalizedString("_delete_old_files_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(row, "Expected 'Delete all files older than..' section is present")
+ }
+
+ func testClearCacheSectionIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let image = UIImage(named: "trashIcon")
+
+ let row = XLFormRowDescriptor(tag: "azzeracache", rowType:XLFormRowDescriptorTypeButton, title: NSLocalizedString("_clear_cache_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(image)
+
+ XCTAssertNotNil(row, "Expected 'Clear Cache' section is present")
+
+ }
+
+ func testLogoutButtonIsPresent() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ let section = XLFormSectionDescriptor.formSection()
+ form.addFormSection(section)
+
+ let image = UIImage(named: "xmark")
+
+ let row = XLFormRowDescriptor(tag: "esci", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_exit_", comment: ""))
+ section.addFormRow(row)
+
+ XCTAssertNotNil(image)
+
+ XCTAssertNotNil(row, "Expected 'Logout' Button is present")
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/Tests/NextcloudUnitTests/SharingTest.swift b/Tests/NextcloudUnitTests/SharingTest.swift
new file mode 100644
index 0000000000..984a4c0ac3
--- /dev/null
+++ b/Tests/NextcloudUnitTests/SharingTest.swift
@@ -0,0 +1,232 @@
+//
+// SharingTest.swift
+// NextcloudTests
+//
+// Created by A200020526 on 07/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+@testable import Nextcloud
+final class SharingTest: XCTestCase {
+
+ var button: UIButton?
+ var ncShare: NCShare?
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ super.setUp()
+ button = UIButton()
+ ncShare = NCShare()
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ button = nil
+ ncShare = nil
+ super.tearDown()
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+
+ //Date exntesion test case
+ func testTomorrow() {
+ let tomorrow = Date.tomorrow
+ let expectedTomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
+ XCTAssertEqual(tomorrow.extendedIso8601String, expectedTomorrow.extendedIso8601String, "Tomorrow date should be correct.")
+ }
+ func testToday() {
+ let today = Date.today
+ let currentDate = Date()
+ XCTAssertEqual(today.extendedIso8601String, currentDate.extendedIso8601String, "Today date should be correct.")
+ }
+
+ func testDayAfter() {
+ let date = Date()
+ let dayAfter = date.dayAfter
+ let expectedDayAfter = Calendar.current.date(byAdding: .day, value: 1, to: date)!
+ XCTAssertEqual(dayAfter.extendedIso8601String, expectedDayAfter.extendedIso8601String, "Day after date should be correct.")
+ }
+
+ //Date Formatter extension Test Case
+ func testShareExpDate() {
+ let dateFormatter = DateFormatter.shareExpDate
+
+ XCTAssertEqual(dateFormatter.formatterBehavior, .behavior10_4, "Formatter behavior should be correct.")
+ XCTAssertEqual(dateFormatter.dateStyle, .medium, "Date style should be correct.")
+ XCTAssertEqual(dateFormatter.dateFormat, NCShareAdvancePermission.displayDateFormat, "Date format should be correct.")
+ }
+
+ //Button Extension test case
+ func testSetBackgroundColor() {
+ // Arrange
+ let color = UIColor.red
+ let state: UIControl.State = .normal
+
+ // Act
+ button?.setBackgroundColor(color, for: state)
+
+ // Assert
+ XCTAssertNotNil(button?.currentBackgroundImage, "Button background image not nil")
+ }
+
+ func testSetBackgroundColorForDifferentStates() {
+ // Arrange
+
+ let selectedColor = UIColor.green
+
+ // Act
+ button?.isSelected = true
+ button?.setBackgroundColor(selectedColor, for: .selected)
+
+ // Assert
+ XCTAssertNotNil(button?.currentBackgroundImage, "Button background image not nil")
+ button?.isSelected = false
+ XCTAssertNil(button?.currentBackgroundImage,"Button background image will be nil")
+ button?.isHighlighted = true
+ XCTAssertNil(button?.currentBackgroundImage, "Button background image will be nil")
+ }
+
+ //UIView extension shadow test case
+ func testAddShadowWithLocation() {
+ // Create a UIView instance
+ let view = UIView()
+
+ // Set the shadow with bottom location
+ view.addShadow(location: .bottom, height: 2, color: .red, opacity: 0.4, radius: 2)
+
+ // Verify that the shadow offset is set correctly for the bottom location
+ let bottomShadowOffset = view.layer.shadowOffset
+ XCTAssertEqual(bottomShadowOffset, CGSize(width: 0, height: 2), "Shadow offset not set correctly for bottom location")
+
+ // Verify that the shadow color is set correctly
+ let shadowColor = view.layer.shadowColor
+ XCTAssertEqual(shadowColor, UIColor.red.cgColor, "Shadow color not set correctly")
+
+ // Verify that the shadow opacity is set correctly
+ let shadowOpacity = view.layer.shadowOpacity
+ XCTAssertEqual(shadowOpacity, 0.4, "Shadow opacity not set correctly")
+
+ // Verify that the shadow radius is set correctly
+ let shadowRadius = view.layer.shadowRadius
+ XCTAssertEqual(shadowRadius, 2.0, "Shadow radius not set correctly")
+ }
+
+ func testAddShadowWithOffset() {
+ // Create a UIView instance
+ let view = UIView()
+
+ // Set the shadow with a custom offset
+ view.addShadow(offset: CGSize(width: 0, height: -4), color: .blue, opacity: 0.6, radius: 3)
+
+ // Verify that the shadow offset is set correctly
+ let shadowOffset = view.layer.shadowOffset
+ XCTAssertEqual(shadowOffset, CGSize(width: 0, height: -4), "Shadow offset not set correctly")
+
+ // Verify that the shadow color is set correctly
+ let shadowColor = view.layer.shadowColor
+ XCTAssertEqual(shadowColor, UIColor.blue.cgColor, "Shadow color not set correctly")
+
+ // Verify that the shadow opacity is set correctly
+ let shadowOpacity = view.layer.shadowOpacity
+ XCTAssertEqual(shadowOpacity, 0.6, "Shadow opacity not set correctly")
+
+ // Verify that the shadow radius is set correctly
+ let shadowRadius = view.layer.shadowRadius
+ XCTAssertEqual(shadowRadius, 3.0, "Shadow radius not set correctly")
+ }
+
+ func testAddShadowForLocation() {
+ // Create a UIView instance
+ let view = UIView()
+
+ // Add shadow to the bottom
+ view.addShadow(location: .bottom, color: UIColor.black)
+
+ // Verify that the shadow properties are set correctly for the bottom location
+ XCTAssertEqual(view.layer.shadowOffset, CGSize(width: 0, height: 2), "Shadow offset not set correctly for bottom location")
+ XCTAssertEqual(view.layer.shadowColor, UIColor.black.cgColor, "Shadow color not set correctly for bottom location")
+ XCTAssertEqual(view.layer.shadowOpacity, 0.4, "Shadow opacity not set correctly for bottom location")
+ XCTAssertEqual(view.layer.shadowRadius, 2.0, "Shadow radius not set correctly for bottom location")
+
+ // Add shadow to the top
+ view.addShadow(location: .top)
+
+ // Verify that the shadow properties are set correctly for the top location
+ XCTAssertEqual(view.layer.shadowOffset, CGSize(width: 0, height: -2), "Shadow offset not set correctly for top location")
+ XCTAssertEqual(view.layer.shadowColor, NCBrandColor.shared.customerDarkGrey.cgColor, "Shadow color not set correctly for top location")
+ XCTAssertEqual(view.layer.shadowOpacity, 0.4, "Shadow opacity not set correctly for top location")
+ XCTAssertEqual(view.layer.shadowRadius, 2.0, "Shadow radius not set correctly for top location")
+ }
+
+ func testAddShadowForOffset() {
+ // Create a UIView instance
+ let view = UIView()
+
+ // Add shadow with custom offset
+ view.addShadow(offset: CGSize(width: 2, height: 2))
+
+ // Verify that the shadow properties are set correctly for the custom offset
+ XCTAssertEqual(view.layer.shadowOffset, CGSize(width: 2, height: 2), "Shadow offset not set correctly for custom offset")
+ XCTAssertEqual(view.layer.shadowColor, UIColor.black.cgColor, "Shadow color not set correctly for custom offset")
+ XCTAssertEqual(view.layer.shadowOpacity, 0.5, "Shadow opacity not set correctly for custom offset")
+ XCTAssertEqual(view.layer.shadowRadius, 5.0, "Shadow radius not set correctly for custom offset")
+ }
+
+
+ func testHasUploadPermission() {
+ // Create an instance of NCShare
+ let share = NCShare()
+
+ // Define the input parameters
+ let tableShareWithUploadPermission = tableShare()
+ tableShareWithUploadPermission.permissions = NCGlobal.shared.permissionMaxFileShare
+
+ let tableShareWithoutUploadPermission = tableShare()
+ tableShareWithoutUploadPermission.permissions = NCGlobal.shared.permissionReadShare
+
+ // Call the hasUploadPermission function
+ let hasUploadPermission1 = share.hasUploadPermission(tableShare: tableShareWithUploadPermission)
+ let hasUploadPermission2 = share.hasUploadPermission(tableShare: tableShareWithoutUploadPermission)
+
+ // Verify the results
+ XCTAssertTrue(hasUploadPermission1, "hasUploadPermission returned false for a tableShare with upload permission")
+ XCTAssertFalse(hasUploadPermission2, "hasUploadPermission returned true for a tableShare without upload permission")
+ }
+
+ func testGetImageShareType() {
+ let sut = NCShareCommon // Replace with the actual class containing the getImageShareType function
+
+ // Test case 1: SHARE_TYPE_USER
+ let shareType1 = sut.shareTypeUser
+ let result1 = sut.getImageShareType(shareType: shareType1)
+ XCTAssertEqual(result1, UIImage(named: "shareTypeEmail")?.imageColor(NCBrandColor.shared.label))
+
+ // Test case 2: SHARE_TYPE_GROUP
+ let shareType2 = sut.shareTypeGroup
+ let result2 = sut.getImageShareType(shareType: shareType2)
+ XCTAssertEqual(result2, UIImage(named: "shareTypeGroup")?.imageColor(NCBrandColor.shared.label))
+
+ // Test case 3: SHARE_TYPE_LINK
+ let shareType3 = sut.shareTypeLink
+ let result3 = sut.getImageShareType(shareType: shareType3)
+ XCTAssertEqual(result3, UIImage(named: "shareTypeLink")?.imageColor(NCBrandColor.shared.label))
+
+ // Test case 4: SHARE_TYPE_EMAIL (with isDropDown=false)
+ let shareType4 = sut.shareTypeEmail
+ let result4 = sut.getImageShareType(shareType: shareType4)
+ XCTAssertEqual(result4, UIImage(named: "shareTypeUser")?.imageColor(NCBrandColor.shared.label))
+
+ // Test case 5: SHARE_TYPE_EMAIL (with isDropDown=true)
+ let shareType5 = sut.shareTypeEmail
+ let isDropDown5 = true
+ let result5 = sut.getImageShareType(shareType: shareType5)//, isDropDown: isDropDown5)
+ XCTAssertEqual(result5, UIImage(named: "email")?.imageColor(NCBrandColor.shared.label))
+ }
+}
diff --git a/iOSClient/Account/NCAccount.swift b/iOSClient/Account/NCAccount.swift
index ecf49a4adf..dd3120bc73 100644
--- a/iOSClient/Account/NCAccount.swift
+++ b/iOSClient/Account/NCAccount.swift
@@ -108,6 +108,10 @@ class NCAccount: NSObject {
// set theming color
NCBrandColor.shared.settingThemingColor(account: account, capabilities: capabilities)
}
+// // Start the auto upload
+// let num = await NCAutoUpload.shared.initAutoUpload(tblAccount: tblAccount)
+// nkLog(start: "Auto upload with \(num) photo")
+
// Networking Process
await NCNetworkingProcess.shared.setCurrentAccount(account)
@@ -193,6 +197,7 @@ class NCAccount: NSObject {
guard let tblAccount = await NCManageDatabase.shared.getTableAccountAsync(predicate: NSPredicate(format: "account == %@", account)) else {
return
}
+
let windowScene = SceneManager.shared.getWindowScene(controller: controller)
await showErrorBanner(windowScene: windowScene, text: String(format: NSLocalizedString("_account_unauthorized_", comment: ""), account), errorCode: NCGlobal.shared.errorUnauthorized401)
diff --git a/iOSClient/Analytics/AnalyticsHelper.swift b/iOSClient/Analytics/AnalyticsHelper.swift
new file mode 100644
index 0000000000..3b765cd06a
--- /dev/null
+++ b/iOSClient/Analytics/AnalyticsHelper.swift
@@ -0,0 +1,90 @@
+//
+// AnalyticsHelper.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 10/06/24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class AnalyticsHelper: NSObject, AnalyticsService {
+
+ @objc static let shared = AnalyticsHelper()
+
+ private var analyticsServices: [AnalyticsService]
+
+ private override init() {
+ // Initialize the analytics services
+ let moEngageAnalytics = MoEngageAnalytics()
+ moEngageAnalytics.trackAppId()
+ analyticsServices = [moEngageAnalytics]
+ super.init()
+ }
+
+ func trackEvent(eventName: AnalyticEvents, properties: [String: Any]? = nil) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackEvent(eventName: eventName, properties: properties) }
+ }
+ }
+
+ func trackUserData() {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackUserData() }
+ }
+ }
+
+ func trackUsedStorageData(quotaUsed: Int64) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackUsedStorageData(quotaUsed: quotaUsed) }
+ }
+ }
+
+ @objc func trackAutoUpload(isEnable: Bool) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackAutoUpload(isEnable: isEnable) }
+ }
+ }
+
+ func trackAppVersion(oldVersion: String?) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackAppVersion(oldVersion: oldVersion) }
+ }
+ }
+
+ func trackLogout() {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackLogout() }
+ }
+ }
+
+ func trackCreateFile(metadata: tableMetadata) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackCreateFile(metadata: metadata) }
+ }
+ }
+
+ func trackEventWithMetadata(eventName: AnalyticEvents, metadata: tableMetadata) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackEventWithMetadata(eventName: eventName, metadata: metadata) }
+ }
+ }
+
+ func trackCreateVoiceMemo(size: Int64, date: Date) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackCreateVoiceMemo(size: size, date: date) }
+ }
+ }
+
+ func trackCreateFolder(isEncrypted: Bool, creationDate: Date) {
+ DispatchQueue.global(qos: .background).async {
+ self.analyticsServices.forEach { $0.trackCreateFolder(isEncrypted: isEncrypted, creationDate: creationDate) }
+ }
+ }
+
+// func displayInAppNotification() {
+// self.analyticsServices.forEach { $0.displayInAppNotification() }
+// }
+
+
+}
diff --git a/iOSClient/Analytics/AnalyticsService.swift b/iOSClient/Analytics/AnalyticsService.swift
new file mode 100644
index 0000000000..7da2e93d45
--- /dev/null
+++ b/iOSClient/Analytics/AnalyticsService.swift
@@ -0,0 +1,98 @@
+//
+// AnalyticsService.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 10/06/24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+protocol AnalyticsService {
+ func trackEvent(eventName: AnalyticEvents, properties: [String: Any]?)
+ func trackUserData()
+ func trackUsedStorageData(quotaUsed: Int64)
+ func trackAutoUpload(isEnable: Bool)
+ func trackAppVersion(oldVersion: String?)
+ func trackLogout()
+ func trackCreateFile(metadata: tableMetadata)
+ func trackCreateVoiceMemo(size: Int64, date: Date)
+ func trackCreateFolder(isEncrypted: Bool, creationDate: Date)
+ func trackEventWithMetadata(eventName: AnalyticEvents, metadata: tableMetadata)
+// func displayInAppNotification()
+}
+
+// swiftlint:disable identifier_name
+enum AnalyticEvents: String {
+ case USER_PROPERTIES_STORAGE_CAPACITY = "storage_capacity" // in GB
+ case USER_PROPERTIES_STORAGE_USED = "storage_used" // % of storage used
+ case USER_PROPERTIES_AUTO_UPLOAD = "auto_upload_on"
+ case USER_PROPERTIES_APP_VERSION = "app_version"
+ case EVENT__ACTION_BUTTON = "action_button_clicked" // when user clicks on fab + button
+ case EVENT__UPLOAD_FILE = "upload_file" // when user uploads any file (not applicable for folder) from other apps
+ case EVENT__CREATE_FILE = "create_file" // when user creates any file in app
+ case EVENT__CREATE_FOLDER = "create_folder"
+ case EVENT__CREATE_VOICE_MEMO = "create_voice_memo"
+ case EVENT__ADD_FAVORITE = "add_favorite"
+ case EVENT__SHARE_FILE = "share_file" // when user share any file using link
+ case EVENT__OFFLINE_AVAILABLE = "offline_available"
+ case EVENT__ONLINE_OFFICE_USED = "online_office_used" // when user opens any office files
+
+ // screen view events when user open specific screen
+ case SCREEN_EVENT__FAVOURITES = "favorites"
+ case SCREEN_EVENT__MEDIA = "medien"
+ case SCREEN_EVENT__OFFLINE_FILES = "offline_files"
+ case SCREEN_EVENT__SHARED = "shared"
+ case SCREEN_EVENT__DELETED_FILES = "deleted_files"
+ case SCREEN_EVENT__NOTIFICATIONS = "notifications"
+
+ var moEngageEvent: String {
+ switch self {
+ default:
+ return self.rawValue
+ }
+ }
+
+ var teliumEvent: String {
+ return self.rawValue
+ }
+
+ var adjustEvent: String {
+ return self.rawValue
+ }
+}
+
+// swiftlint:disable identifier_name
+enum AnalyticPropertyAttributes: String {
+ // properties attributes key
+ case PROPERTIES__FILE_TYPE = "file_type"
+ case PROPERTIES__FOLDER_TYPE = "folder_type"
+ case PROPERTIES__FILE_SIZE = "file_size" // in MB
+ case PROPERTIES__CREATION_DATE = "creation_date" // yyyy-MM-dd
+ case PROPERTIES__UPLOAD_DATE = "upload_date" // // yyyy-MM-dd
+}
+
+enum FolderType: String {
+ // properties attributes key
+ case FOLDER_ENCRYPTED = "encrypted"
+ case FOLDER_NORMAL = "not encrypted"
+}
+
+
+enum Size {
+ static let KILOBYTE = 1024
+ static let MEGABYTE = KILOBYTE * 1024
+ static let GIGABYTE = MEGABYTE * 1024
+}
+
+enum FileType: String {
+ case FOTO = "foto"
+ case AUDIO = "audio"
+ case VIDEO = "video"
+ case PDF = "pdf"
+ case TEXT = "text"
+ case DOCX = "docx"
+ case XLSX = "xlsx"
+ case PPT = "ppt"
+ case OTHER = "other"
+}
diff --git a/iOSClient/Analytics/MoEngage/MoEngageAnalytics.swift b/iOSClient/Analytics/MoEngage/MoEngageAnalytics.swift
new file mode 100644
index 0000000000..b10abb9abc
--- /dev/null
+++ b/iOSClient/Analytics/MoEngage/MoEngageAnalytics.swift
@@ -0,0 +1,441 @@
+//
+// MoEngageAnalytics.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 10/06/24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import MoEngageSDK
+import MoEngageInApps
+import StoreKit
+import OSLog
+
+class MoEngageAnalytics: NSObject {
+ static let shared = MoEngageAnalytics()
+ private let log = Logger(subsystem: Bundle.main.bundleIdentifier ?? "MoEngage", category: "MoEngageAnalytics")
+
+ // Initializer for the MoEngageAnalytics class
+ override init() {
+ super.init()
+
+ log.debug("Initializing MoEngage SDK…")
+
+ // Create a configuration object for MoEngage SDK with the given App ID and Data Center
+ let sdkConfig = MoEngageSDKConfig(appId: "7KWWUKA6OKXGP8Q6DMCXLDX5", dataCenter: MoEngageDataCenter.data_center_02)
+
+ // Disable periodic flushing of analytics data
+ sdkConfig.analyticsDisablePeriodicFlush = true
+
+ // Initialize the MoEngage SDK
+ // Use different initialization methods for Debug and Production environments
+
+#if DEBUG
+ MoEngage.sharedInstance.initializeDefaultTestInstance(sdkConfig)
+#else
+ MoEngage.sharedInstance.initializeDefaultLiveInstance(sdkConfig)
+#endif
+ log.debug("MoEngage SDK initializeDefault instance set (debug: \( self._isDebugBuild() ))")
+
+ Task { @MainActor in
+ setupMoEngageInAppMessaging()
+ }
+
+ // Register delegate for In-App Native callbacks
+ MoEngageSDKInApp.sharedInstance.setInAppDelegate(self)
+ log.debug("MoEngage In-App delegate set")
+ }
+
+ // MARK: - Setup Helper
+ /// Call this early in app lifecycle (e.g., AppDelegate didFinishLaunching or SceneDelegate willConnect)
+ static func setupIfNeeded() {
+ _ = MoEngageAnalytics.shared
+ }
+
+ // MARK: - UI Helpers
+ private func activeWindowScene() -> UIWindowScene? {
+ return UIApplication.shared.connectedScenes
+ .compactMap { $0 as? UIWindowScene }
+ .first { $0.activationState == .foregroundActive }
+ }
+
+ private func topViewController(in root: UIViewController?) -> UIViewController? {
+ guard let root = root else { return nil }
+ if let nav = root as? UINavigationController { return topViewController(in: nav.visibleViewController) }
+ if let tab = root as? UITabBarController { return topViewController(in: tab.selectedViewController) }
+ if let presented = root.presentedViewController { return topViewController(in: presented) }
+ return root
+ }
+
+ @MainActor
+ func currentTopViewController() -> UIViewController? {
+ // 1. Find the active window scene (Crucial for iPad)
+ let activeScene = UIApplication.shared.connectedScenes
+ .compactMap { $0 as? UIWindowScene }
+ .first { $0.activationState == .foregroundActive }
+
+ // 2. Get the key window from that specific scene
+ let keyWindow = activeScene?.windows.first { $0.isKeyWindow }
+ ?? activeScene?.windows.first
+
+ // 3. Start from the rootViewController
+ var topController = keyWindow?.rootViewController
+
+ // 4. Drill down through navigation, tabs, and presented controllers
+ while let presentedController = topController?.presentedViewController {
+ topController = presentedController
+ }
+
+ return topController
+ }
+
+ /// Safer wrapper that ensures visible scene and top VC before asking MoEngage to show in-apps
+ @MainActor
+ func displayInAppNotificationSafely(reason: String? = nil) {
+ let reasonText = reason ?? "unspecified"
+
+ // 1. Get all connected window scenes
+ let scenes = UIApplication.shared.connectedScenes
+ .compactMap { $0 as? UIWindowScene }
+
+ // 2. Prioritize the scene that actually has the "Key Window" (Works best for iPad Split View)
+ let activeScene = scenes.first { $0.windows.contains(where: { $0.isKeyWindow }) }
+ ?? scenes.first { $0.activationState == .foregroundActive }
+
+ guard let scene = activeScene else {
+ print("In-App not shown: No foreground window scene (reason: \(reasonText))")
+ return
+ }
+
+ // 3. Ensure we have a valid top controller
+ guard let topVC = currentTopViewController() else {
+ print("In-App not shown: No top view controller available (reason: \(reasonText))")
+ return
+ }
+
+ // 4. Trigger MoEngage
+ // iPad Tip: MoEngage uses the key window to determine where to draw the UI
+ if UIDevice.current.userInterfaceIdiom != .pad{
+ self.triggerMoEngage()
+ }
+ else {
+#if targetEnvironment(simulator)
+ // Simulator: Always show fallback alert
+ print("In-App shown: showInApp)")
+ let appID = "312838242"
+
+ // Correct URLs with /app/id/ prefix
+ let webURLString = "https://apps.apple.com\(appID)?action=write-review"
+
+ self.showSimulatorAlert(link: webURLString)
+#else
+ // iPad Fallback: Scene might be transitioning. Retry once.
+ print("iPad Scene transition detected. Retrying...")
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+ self.triggerMoEngage()
+ }
+#endif
+ }
+ }
+
+ private func triggerMoEngage() {
+ MoEngageSDKInApp.sharedInstance.showInApp()
+ MoEngageSDKInApp.sharedInstance.showNudge()
+ }
+
+
+ private func _isDebugBuild() -> Bool {
+ #if DEBUG
+ return true
+ #else
+ return false
+ #endif
+ }
+
+ // Method to track the App ID
+ func trackAppId() {
+ MoEngageSDKAnalytics.sharedInstance.trackLocale(forAppID: "312838242")
+ }
+
+ @MainActor func setupMoEngageInAppMessaging() {
+ // MARK: MoEngage In-App messages
+ log.debug("setupMoEngageInAppMessaging() — scheduling initial in-app evaluation")
+ Task { @MainActor in
+ displayInAppNotificationSafely(reason: "initial setup")
+ }
+ }
+
+ // Helper to show the alert on the topmost view controller
+ private func showSimulatorAlert(link: String) {
+ let alert = UIAlertController(
+ title: "Simulator Mode",
+ message: "App Store links don't open in Simulator. On a real device, this would open: \(link)",
+ preferredStyle: .alert
+ )
+ alert.addAction(UIAlertAction(title: "OK", style: .default))
+
+ // Finds the current active window to present the alert
+ let keyWindow = UIApplication.shared.connectedScenes
+ .compactMap { $0 as? UIWindowScene }
+ .flatMap { $0.windows }
+ .first { $0.isKeyWindow }
+
+ keyWindow?.rootViewController?.present(alert, animated: true)
+ }
+
+}
+
+// AnalyticsService protocol
+extension MoEngageAnalytics: AnalyticsService {
+ // Method to track a specific event with optional properties
+ func trackEvent(eventName: AnalyticEvents, properties: [String: Any]?) {
+ let eventProperties = MoEngageProperties(withAttributes: properties)
+ MoEngageSDKAnalytics.sharedInstance.trackEvent(eventName.moEngageEvent, withProperties: eventProperties)
+ Task { @MainActor in
+ // Re-evaluate in-app messages after trackEvent
+ MoEngageAnalytics.shared.displayInAppNotificationSafely(reason: "track Event")
+ }
+
+ }
+
+ // Method to track user data
+ func trackUserData() {
+ // Get the active user account from the database
+ guard let user = NCManageDatabase.shared.getActiveTableAccount() else { return }
+
+ // Set user attributes in the MoEngage SDK
+ MoEngageSDKAnalytics.sharedInstance.setUniqueID(user.userId)
+ MoEngageSDKAnalytics.sharedInstance.setName(user.displayName)
+ MoEngageSDKAnalytics.sharedInstance.setEmailID(user.email)
+
+ // Convert the user's total storage quota to a readable format and set it as a user attribute
+ let storageCapacity = NCUtilityFileSystem().transformedSize(user.quotaTotal)
+ MoEngageSDKAnalytics.sharedInstance.setUserAttribute(storageCapacity, withAttributeName: AnalyticEvents.USER_PROPERTIES_STORAGE_CAPACITY.rawValue)
+
+ // Track whether auto-upload is enabled for the user
+ trackAutoUpload(isEnable: user.autoUploadStart)
+ }
+
+ // Method to track the used storage data
+ func trackUsedStorageData(quotaUsed: Int64) {
+ MoEngageSDKAnalytics.sharedInstance.setUserAttribute(quotaUsed, withAttributeName: AnalyticEvents.USER_PROPERTIES_STORAGE_USED.rawValue)
+ }
+
+ // Method to track the auto-upload setting
+ func trackAutoUpload(isEnable: Bool) {
+ if isEnable {
+ MoEngageSDKAnalytics.sharedInstance.setUserAttribute(isEnable, withAttributeName: AnalyticEvents.USER_PROPERTIES_AUTO_UPLOAD.rawValue)
+ }
+ }
+
+ // Method to track the app version
+ func trackAppVersion(oldVersion: String?) {
+ // Get the app version and set it as a user attribute
+ let version = NCUtility().getVersionBuild() as String
+
+ // Check if a build version key is present in UserDefaults
+ if let oldVersion {
+ if version != oldVersion {
+ MoEngageSDKAnalytics.sharedInstance.appStatus(.update)
+ if let oldAppVersion = Int(oldVersion.dropLast().replacingOccurrences(of: ".", with: "")) {
+ if oldAppVersion < NCGlobal.shared.moEngageAppVersion {
+ trackUserData()
+ }
+ }
+ }
+ } else {
+ MoEngageSDKAnalytics.sharedInstance.appStatus(.install)
+ }
+
+ MoEngageSDKAnalytics.sharedInstance.setUserAttribute(version, withAttributeName: AnalyticEvents.USER_PROPERTIES_APP_VERSION.rawValue)
+ }
+
+ //Method to track user logout
+ func trackLogout() {
+ MoEngageSDKAnalytics.sharedInstance.resetUser()
+ }
+
+ //Method to track create file
+ func trackCreateFile(metadata: tableMetadata) {
+ let properties = MoEngageProperties()
+ properties.addAttribute(getFileType(contentType: metadata.contentType), withName: AnalyticPropertyAttributes.PROPERTIES__FILE_TYPE.rawValue)
+ properties.addAttribute(String(getFileSizeInMB(bytes: Int(metadata.size))), withName: AnalyticPropertyAttributes.PROPERTIES__FILE_SIZE.rawValue)
+ properties.addAttribute(getDate(date: metadata.creationDate as Date), withName: AnalyticPropertyAttributes.PROPERTIES__CREATION_DATE.rawValue)
+ MoEngageSDKAnalytics.sharedInstance.trackEvent(AnalyticEvents.EVENT__CREATE_FILE.rawValue, withProperties: properties)
+ }
+
+ //Method to track upload file
+ func trackEventWithMetadata(eventName: AnalyticEvents, metadata: tableMetadata) {
+ let properties = MoEngageProperties()
+ properties.addAttribute(getFileType(contentType: metadata.contentType), withName: AnalyticPropertyAttributes.PROPERTIES__FILE_TYPE.rawValue)
+ properties.addAttribute(String(getFileSizeInMB(bytes: Int(metadata.size))), withName: AnalyticPropertyAttributes.PROPERTIES__FILE_SIZE.rawValue)
+ properties.addAttribute(getDate(date: metadata.creationDate as Date), withName: AnalyticPropertyAttributes.PROPERTIES__CREATION_DATE.rawValue)
+ properties.addAttribute(getDate(date: metadata.uploadDate as Date), withName: AnalyticPropertyAttributes.PROPERTIES__UPLOAD_DATE.rawValue)
+ MoEngageSDKAnalytics.sharedInstance.trackEvent(eventName.rawValue, withProperties: properties)
+ }
+
+ //Method to track create folder
+ func trackCreateFolder(isEncrypted: Bool, creationDate: Date) {
+ let properties = MoEngageProperties()
+ properties.addAttribute(isEncrypted ? FolderType.FOLDER_ENCRYPTED.rawValue : FolderType.FOLDER_NORMAL.rawValue , withName: AnalyticPropertyAttributes.PROPERTIES__FILE_TYPE.rawValue)
+ properties.addAttribute(getDate(date: creationDate), withName: AnalyticPropertyAttributes.PROPERTIES__CREATION_DATE.rawValue)
+ MoEngageSDKAnalytics.sharedInstance.trackEvent(AnalyticEvents.EVENT__CREATE_FOLDER.rawValue, withProperties: properties)
+ }
+
+ //Method to track create voice memo
+ func trackCreateVoiceMemo(size: Int64, date: Date) {
+ let properties = MoEngageProperties()
+ properties.addAttribute(FileType.AUDIO.rawValue, withName: AnalyticPropertyAttributes.PROPERTIES__FILE_TYPE.rawValue)
+ properties.addAttribute(String(getFileSizeInMB(bytes: Int(size))), withName: AnalyticPropertyAttributes.PROPERTIES__FILE_SIZE.rawValue)
+ properties.addAttribute(getDate(date: date), withName: AnalyticPropertyAttributes.PROPERTIES__CREATION_DATE.rawValue)
+ MoEngageSDKAnalytics.sharedInstance.trackEvent(AnalyticEvents.EVENT__CREATE_VOICE_MEMO.rawValue, withProperties: properties)
+ }
+}
+
+// Functions
+extension MoEngageAnalytics {
+ private func getFileType(contentType: String) -> String? {
+ switch contentType {
+ case "image/png":
+ return FileType.FOTO.rawValue
+ case "audio/x-m4a", "audio/mp4":
+ return FileType.AUDIO.rawValue
+ case "video/mp4":
+ return FileType.VIDEO.rawValue
+ case "application/pdf":
+ return FileType.PDF.rawValue
+ case "text/x-markdown","text/plain":
+ return FileType.TEXT.rawValue
+ case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
+ return FileType.DOCX.rawValue
+ case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","text/csv":
+ return FileType.XLSX.rawValue
+ case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
+ return FileType.PPT.rawValue
+ default:
+ return FileType.OTHER.rawValue
+ }
+ }
+
+ private func getDate(date: Date) -> String {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "yyyy-MM-dd"
+ return dateFormatter.string(from: date)
+ }
+
+ private func getFileSizeInMB(bytes: Int) -> Float {
+ return round((Float(bytes) / Float(Size.MEGABYTE)) * 10) / 10
+ }
+
+}
+
+// MARK: - MoEngage In-App Native Delegate
+extension MoEngageAnalytics: MoEngageInAppNativeDelegate {
+
+ // Called when user clicks an in-app with navigation action (e.g., deep link)
+ func inAppClicked(withCampaignInfo inappCampaign: MoEngageInAppCampaign,
+ andNavigationActionInfo navigationAction: MoEngageInAppNavigationAction,
+ forAccountMeta accountMeta: MoEngageAccountMeta) {
+ // handle navigation actions if needed
+ }
+
+ // Called when user clicks an in-app with custom action (e.g., our rating trigger)
+ func inAppClicked(withCampaignInfo inappCampaign: MoEngageInAppCampaign,
+ andCustomActionInfo customAction: MoEngageInAppAction,
+ forAccountMeta accountMeta: MoEngageAccountMeta) {
+
+ let kv = customAction.keyValuePairs
+ log.debug("In-App custom action received: keyValues=\(kv)")
+
+ if let showRating = kv["show-native-rating"] as? String,
+ showRating.lowercased() == "true" {
+ log.debug("User clicked Rating - Opening App Store directly")
+
+ // Use the reliable direct link instead of the restricted native prompt
+ ReviewManager.shared.requestReviewIfAppropriate()
+ }
+ }
+
+ // Called when a "self-handled" in-app is triggered
+ func selfHandledInAppTriggered(withInfo inAppCampaign: MoEngageInAppSelfHandledCampaign,
+ forAccountMeta accountMeta: MoEngageAccountMeta) {
+ log.debug("Self-handled in-app triggered: \(String(describing: inAppCampaign))")
+ }
+
+ // Optional — track impression
+ func inAppShown(withCampaignInfo inappCampaign: MoEngageInAppCampaign,
+ forAccountMeta accountMeta: MoEngageAccountMeta) {
+ log.debug("In-App shown: \(String(describing: inappCampaign))")
+ }
+
+ // Optional — track dismissal
+ func inAppDismissed(withCampaignInfo inappCampaign: MoEngageInAppCampaign,
+ forAccountMeta accountMeta: MoEngageAccountMeta) {
+ log.debug("In-App dismissed: \(String(describing: inappCampaign))")
+ }
+}
+
+@MainActor // Ensures this is only called on the Main Thread
+class UIHelper {
+
+ static func getTopViewController() -> UIViewController? {
+ // 1. Get the active scene
+ let activeScene = UIApplication.shared.connectedScenes
+ .first { $0.activationState == .foregroundActive } as? UIWindowScene
+
+ // 2. Get the root view controller from the key window
+ let rootVC = activeScene?.windows
+ .first { $0.isKeyWindow }?.rootViewController
+
+ return findTopViewController(from: rootVC)
+ }
+
+ private static func findTopViewController(from root: UIViewController?) -> UIViewController? {
+ if let nav = root as? UINavigationController {
+ return findTopViewController(from: nav.visibleViewController)
+ }
+ if let tab = root as? UITabBarController {
+ return findTopViewController(from: tab.selectedViewController)
+ }
+ if let presented = root?.presentedViewController {
+ return findTopViewController(from: presented)
+ }
+ return root
+ }
+}
+
+class ReviewManager {
+ static let shared = ReviewManager()
+ private let appID = "312838242"
+
+ /// Requests the native system rating sheet if an active foreground scene is available.
+ func requestReviewIfAppropriate() {
+ // 1. Find the active, foreground UIWindowScene required by StoreKit
+ guard let scene = UIApplication.shared.connectedScenes
+ .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
+ // Silently exit if no foreground view is active
+// // Fallback: If no scene is active (rare), open the App Store directly
+// self.forceOpenAppStore()
+ return
+ }
+
+ // 2. Trigger the native system prompt overlay
+ // Note: Apple will throttle this to 3 times per year maximum per user.
+ SKStoreReviewController.requestReview(in: scene)
+ }
+
+ /// Call this function ONLY when a user explicitly taps a manual "Rate Us" button.
+ func forceOpenAppStore() {
+ let appStoreURLString = "itms-apps://://apple.com\(appID)?action=write-review"
+ let webURLString = "https://apple.com\(appID)?action=write-review"
+
+ if let url = URL(string: appStoreURLString), UIApplication.shared.canOpenURL(url) {
+ // Opens the native iOS App Store app directly to your review composition page
+ UIApplication.shared.open(url, options: [:], completionHandler: nil)
+ } else if let fallbackWebUrl = URL(string: webURLString) {
+ // Fallback safety layer: Opens up your App Store page inside Safari
+ UIApplication.shared.open(fallbackWebUrl, options: [:], completionHandler: nil)
+ }
+ }
+}
diff --git a/iOSClient/AppUpdate/AppUpdater.swift b/iOSClient/AppUpdate/AppUpdater.swift
new file mode 100644
index 0000000000..c091a9323d
--- /dev/null
+++ b/iOSClient/AppUpdate/AppUpdater.swift
@@ -0,0 +1,104 @@
+//
+// AppUpdater.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 09/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import FirebaseRemoteConfig
+
+struct AppUpdaterKey {
+ static let lastUpdateCheckDate : String = "lastUpdateCheckDate"
+}
+
+class AppUpdater {
+ func checkForUpdate() {
+ checkUpdate{ (version, isForceupdate) in
+ DispatchQueue.main.async {
+ if let version = version, let isForceupdate = isForceupdate {
+ if (isForceupdate) {
+ self.showUpdateAlert(version: version, isForceUpdate: isForceupdate)
+ } else {
+ if self.checkLastUpdate() {
+ self.showUpdateAlert(version: version, isForceUpdate: isForceupdate)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ func showUpdateAlert(version: String, isForceUpdate: Bool) {
+ guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let viewControlller = appDelegate.window?.rootViewController else { return }
+ let descriptionMsg = String(format: NSLocalizedString("update_description", comment: ""), version)
+ let alert = UIAlertController(title: NSLocalizedString("update_available", comment: ""), message: descriptionMsg, preferredStyle: .alert)
+ let updateAction = UIAlertAction(title: NSLocalizedString("update", comment: ""), style: .default, handler: { action in
+ guard let url = URL(string: NCBrandOptions.shared.appStoreUrl) else { return }
+ UIApplication.shared.open(url)
+ })
+ alert.addAction(updateAction)
+ if !isForceUpdate {
+ alert.addAction(UIAlertAction(title: NSLocalizedString("not_now", comment: ""), style: .default, handler: { action in
+ self.saveAppUpdateCheckDate()
+ }))
+ }
+ alert.preferredAction = updateAction
+ viewControlller.present(alert, animated: true, completion: {})
+ }
+
+ func checkLastUpdate() -> Bool {
+ if let lastUpdateCheckDate = UserDefaults.standard.object(forKey: AppUpdaterKey.lastUpdateCheckDate) as? Date {
+ return daysBetweenDate(from: lastUpdateCheckDate) > 7
+ } else {
+ return true
+ }
+ }
+
+ func checkUpdate(completion: @escaping (String?, Bool?) -> Void) {
+ let remoteConfig = RemoteConfig.remoteConfig()
+ remoteConfig.fetch(withExpirationDuration: 1) { (status, error) in
+ if status == .success {
+ remoteConfig.activate { value, error in
+ // Remote config values fetched successfully
+ let iOSVersionString = remoteConfig["ios_app_version"].stringValue ?? "default_value"
+ let isForcheUpdate = remoteConfig["ios_force_update"].boolValue
+ if let currentVersionString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+ let currentVersion = Int(currentVersionString.replacingOccurrences(of: ".", with: "")),
+ let iOSVersion = Int(iOSVersionString.replacingOccurrences(of: ".", with: "")) {
+// if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+ if iOSVersion != currentVersion {
+ // There is an update available
+ completion(iOSVersionString, isForcheUpdate)
+ } else {
+ completion(nil, nil)
+ }
+ }
+// let iOSVersion = remoteConfig["ios_app_version"].stringValue ?? "default_value"
+// let isForcheUpdate = remoteConfig["ios_force_update"].boolValue
+// if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+// if iOSVersion != currentVersion {
+// // There is an update available
+// completion(iOSVersion,isForcheUpdate)
+// } else {
+// completion(nil, nil)
+// }
+// }
+ }
+ } else {
+ // Handle error
+ print("Error fetching remote config: \(error?.localizedDescription ?? "Unknown error")")
+ completion(nil, nil)
+ }
+ }
+ }
+
+ func saveAppUpdateCheckDate() {
+ UserDefaults.standard.setValue(Date(), forKey: AppUpdaterKey.lastUpdateCheckDate)
+ }
+
+ func daysBetweenDate(from date: Date) -> Int {
+ return Calendar.current.dateComponents([.day], from: date, to: Date()).day ?? 0
+ }
+}
diff --git a/iOSClient/AppUtility.swift b/iOSClient/AppUtility.swift
new file mode 100644
index 0000000000..6d940416ca
--- /dev/null
+++ b/iOSClient/AppUtility.swift
@@ -0,0 +1,23 @@
+//
+// AppUtility.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 17/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+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")
+ }
+}
diff --git a/iOSClient/EmptyView/NCEmptyDataSet.swift b/iOSClient/EmptyView/NCEmptyDataSet.swift
new file mode 100644
index 0000000000..389a11d651
--- /dev/null
+++ b/iOSClient/EmptyView/NCEmptyDataSet.swift
@@ -0,0 +1,129 @@
+//
+// NCEmptyDataSet.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 19/10/2020.
+// Copyright © 2020 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+
+public protocol NCEmptyDataSetDelegate: AnyObject {
+ func emptyDataSetView(_ view: NCEmptyView)
+}
+
+// optional func
+public extension NCEmptyDataSetDelegate {
+ func emptyDataSetView(_ view: NCEmptyView) {}
+}
+
+class NCEmptyDataSet: NSObject {
+
+ private var emptyView: NCEmptyView?
+ private var timer: Timer?
+ private var numberItemsForSections: Int = 0
+ private weak var delegate: NCEmptyDataSetDelegate?
+
+ private var fillBackgroundName: String = ""
+ private var fillBackgroundView = UIImageView()
+
+ private var centerXAnchor: NSLayoutConstraint?
+ private var centerYAnchor: NSLayoutConstraint?
+
+ init(view: UIView, offset: CGFloat = 0, delegate: NCEmptyDataSetDelegate?) {
+ super.init()
+
+ guard let emptyView = NCEmptyView.fromNib().instantiate(withOwner: self, options: nil).first as? NCEmptyView else { return }
+
+ self.delegate = delegate
+ self.emptyView = emptyView
+
+ emptyView.isHidden = true
+ emptyView.translatesAutoresizingMaskIntoConstraints = false
+
+ view.addSubview(emptyView)
+
+ emptyView.widthAnchor.constraint(equalToConstant: 350).isActive = true
+ emptyView.heightAnchor.constraint(equalToConstant: 250).isActive = true
+
+ if let view = view.superview {
+ centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
+ centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset)
+ } else {
+ centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
+ centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset)
+ }
+
+ centerXAnchor?.isActive = true
+ centerYAnchor?.isActive = true
+
+ }
+
+ func setOffset(_ offset: CGFloat) {
+
+ centerYAnchor?.constant = offset
+ }
+
+ func numberOfItemsInSection(_ num: Int, section: Int) {
+
+ if section == 0 {
+ numberItemsForSections = num
+ } else {
+ numberItemsForSections += num
+ }
+
+ if let emptyView = emptyView {
+
+ self.delegate?.emptyDataSetView(emptyView)
+
+ if !(timer?.isValid ?? false) && emptyView.isHidden == true {
+ timer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(timerHandler(_:)), userInfo: nil, repeats: false)
+ }
+
+ if numberItemsForSections > 0 {
+ self.emptyView?.isHidden = true
+ }
+ }
+ }
+
+ @objc func timerHandler(_ timer: Timer) {
+
+ if numberItemsForSections == 0 {
+ self.emptyView?.isHidden = false
+ } else {
+ self.emptyView?.isHidden = true
+ }
+ }
+}
+
+public class NCEmptyView: UIView {
+
+ @IBOutlet weak var emptyImage: UIImageView!
+ @IBOutlet weak var emptyTitle: UILabel!
+ @IBOutlet weak var emptyDescription: UILabel!
+
+ static func fromNib() -> UINib {
+ return UINib(nibName: "NCEmptyView", bundle: nil)
+ }
+
+ public override func awakeFromNib() {
+ super.awakeFromNib()
+
+ emptyTitle.textColor = .label
+ }
+}
diff --git a/iOSClient/EmptyView/NCEmptyView.xib b/iOSClient/EmptyView/NCEmptyView.xib
new file mode 100644
index 0000000000..0b5ebbe133
--- /dev/null
+++ b/iOSClient/EmptyView/NCEmptyView.xib
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Helper/AdjustHelper.h b/iOSClient/Helper/AdjustHelper.h
new file mode 100644
index 0000000000..9b63269a17
--- /dev/null
+++ b/iOSClient/Helper/AdjustHelper.h
@@ -0,0 +1,48 @@
+//
+// AdjustHelper.h
+// Nextcloud
+//
+// Created by A200073704 on 17/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+#import
+//#import
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef enum : NSUInteger {
+ Login,
+ LoginSuccessful,
+ Sharing,
+ CreateLink,
+ DocumentScan,
+ CameraUpload,
+ FileUpload,
+ UseCamera,
+ Logout,
+ ResetsApp,
+ AutomaticUploadPhotosOn,
+ AutomaticUploadPhotosOff,
+ ManualBackup,
+ AutomaticBackup
+} TriggerEvent;
+
+@interface AdjustHelper : NSObject
+
+@property (nonatomic, strong) NSString *yourAppToken;
+@property (nonatomic, strong) NSString *environment;
+@property (nonatomic, strong) ADJConfig *adjustConfig;
+@property (nonatomic, strong) ADJEvent *eventLogin;
+@property (nonatomic, strong) ADJEvent *event;
+@property (nonatomic, assign) TriggerEvent triggerEvent;
+
+-(void)configAdjust;
+-(void)subsessionEnd;
+-(void)subsessionStart;
+-(void)trackLogin;
+-(void)trackEvent:(TriggerEvent)event;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOSClient/Helper/AdjustHelper.m b/iOSClient/Helper/AdjustHelper.m
new file mode 100644
index 0000000000..780be4fe8e
--- /dev/null
+++ b/iOSClient/Helper/AdjustHelper.m
@@ -0,0 +1,154 @@
+//
+// AdjustHelper.m
+// Nextcloud
+//
+// Created by A200073704 on 17/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+#import "AdjustHelper.h"
+//#import
+
+@implementation AdjustHelper
+
+-(void)configAdjust {
+ self.yourAppToken = @"1zfaxn19pd7k";
+ self.environment = ADJEnvironmentProduction;
+ self.adjustConfig = [[ADJConfig alloc] initWithAppToken:self.yourAppToken
+ environment:self.environment];
+ [self.adjustConfig setLogLevel:ADJLogLevelVerbose];
+ [Adjust initSdk:self.adjustConfig];
+}
+
+-(void)subsessionEnd {
+ [Adjust trackSubsessionEnd];
+}
+
+-(void)subsessionStart {
+ [Adjust trackSubsessionStart];
+}
+
+- (void)trackLogin {
+ self.eventLogin = [[ADJEvent alloc] initWithEventToken:@"p8hl78"];
+// self.eventLogin = [ADJEvent eventWithEventToken:@"p8hl78"];
+ [Adjust trackEvent: self.eventLogin];
+}
+
+-(void)trackEvent:(TriggerEvent)event {
+// self.event = [ADJEvent eventWithEventToken:@"gb97gb"];
+ BOOL track = [[NSUserDefaults standardUserDefaults] valueForKey:@"isAnalysisDataCollectionSwitchOn"];
+ if (!track) {
+ return;
+ }
+
+ switch(event){
+ case Login:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"p8hl78"];
+// event = [ADJEvent eventWithEventToken:@"gb97gb"];
+// self.event = [ADJEvent eventWithEventToken:@"gb97gb"];
+ [Adjust trackEvent:event];
+ }
+ break;
+ case LoginSuccessful:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"dowf81"];
+ // event = [ADJEvent eventWithEventToken:@"gb97gb"];
+ // self.event = [ADJEvent eventWithEventToken:@"gb97gb"];
+ [Adjust trackEvent:event];
+ }
+ break;
+ case Sharing:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"mbeoyd"];
+ [Adjust trackEvent:event];
+ NSLog(@"%@", [event debugDescription]);
+ }
+// self.event = [ADJEvent eventWithEventToken:@"fqtiu7"];
+ break;
+ case CreateLink:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"29b333"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"qeyql3"];
+ break;
+ case DocumentScan:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"im963g"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"7fec8n"];
+ break;
+ case CameraUpload:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"hf9paq"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"v1g6ly"];
+ break;
+ case FileUpload:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"hsbk2m"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"4rd8r4"];
+ break;
+ case UseCamera:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"3czack"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"3czack"];
+ break;
+ case Logout:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"n5q2qu"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"g6mj9y"];
+ break;
+ case ResetsApp:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"3sj1xp"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"zi18r0"];
+ break;
+ case AutomaticUploadPhotosOn:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"5lxz6h"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"vwd9yk"];
+ break;
+ case AutomaticUploadPhotosOff:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"176496"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"e95w5t"];
+ break;
+ case ManualBackup:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"oojr4y"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"oojr4y"];
+ break;
+ case AutomaticBackup:
+ {
+ ADJEvent *event = [[ADJEvent alloc] initWithEventToken:@"7dkhkx"];
+ [Adjust trackEvent:event];
+ }
+// self.event = [ADJEvent eventWithEventToken:@"7dkhkx"];
+ break;
+
+ default :
+ break;
+ }
+// [Adjust trackEvent: self.event];
+}
+
+@end
+
diff --git a/iOSClient/Helper/TealiumHelper.swift b/iOSClient/Helper/TealiumHelper.swift
new file mode 100644
index 0000000000..9680da8e0a
--- /dev/null
+++ b/iOSClient/Helper/TealiumHelper.swift
@@ -0,0 +1,56 @@
+//
+// TealiumHelper.swift
+// Nextcloud
+//
+// Created by A200073704 on 17/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+import TealiumCore
+import TealiumLifecycle
+import TealiumAutotracking
+import TealiumLocation
+import TealiumRemoteCommands
+import TealiumTagManagement
+import TealiumVisitorService
+import Foundation
+
+class TealiumHelper: NSObject {
+ static let shared = TealiumHelper()
+ let config = TealiumConfig(account: "telekom",
+ profile: "magentacloud-app",
+ environment: "dev")
+ var tealium: Tealium?
+
+ @objc override init() {
+
+ config.batchingEnabled = true
+ config.logLevel = .debug
+
+ config.collectors = [Collectors.Lifecycle,
+ Collectors.Location,
+ Collectors.VisitorService]
+
+ config.dispatchers = [Dispatchers.TagManagement,
+ Dispatchers.RemoteCommands]
+
+ tealium = Tealium(config: config, enableCompletion: {value in
+ print(value)
+ }
+ )
+ }
+
+ func start() {
+ _ = TealiumHelper.shared
+ }
+
+ @objc func trackView(title: String, data: [String: Any]?) {
+ let tealView = TealiumView(title, dataLayer: data)
+ TealiumHelper.shared.tealium?.track(tealView)
+ }
+
+ @objc func trackEvent(title: String, data: [String: Any]?) {
+ let tealEvent = TealiumEvent(title, dataLayer: data)
+ TealiumHelper.shared.tealium?.track(tealEvent)
+ }
+}
+
diff --git a/iOSClient/Login/NCLoginWeb.swift b/iOSClient/Login/NCLoginWeb.swift
new file mode 100644
index 0000000000..0f19c2a9c2
--- /dev/null
+++ b/iOSClient/Login/NCLoginWeb.swift
@@ -0,0 +1,433 @@
+//
+// NCLoginWeb.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 21/08/2019.
+// Copyright © 2019 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import WebKit
+import NextcloudKit
+import FloatingPanel
+
+class NCLoginWeb: UIViewController {
+
+ var webView: WKWebView?
+ var newWebView: WKWebView?
+
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let utility = NCUtility()
+
+ var titleView: String = ""
+
+ var urlBase = ""
+ var user: String?
+
+ var configServerUrl: String?
+ var configUsername: String?
+ var configPassword: String?
+ var configAppPassword: String?
+
+ var loginFlowV2Available = false
+ var loginFlowV2Token = ""
+ var loginFlowV2Endpoint = ""
+ var loginFlowV2Login = ""
+
+ /// Controller
+ var controller: NCMainTabBarController?
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0
+
+ // load AppConfig
+ if (NCBrandOptions.shared.disable_multiaccount == false) || (NCBrandOptions.shared.disable_multiaccount == true && accountCount == 0) {
+ if let configurationManaged = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), NCBrandOptions.shared.use_AppConfig {
+
+ if let serverUrl = configurationManaged[NCGlobal.shared.configuration_serverUrl] as? String {
+ self.configServerUrl = serverUrl
+ }
+ if let username = configurationManaged[NCGlobal.shared.configuration_username] as? String, !username.isEmpty, username.lowercased() != "username" {
+ self.configUsername = username
+ }
+ if let password = configurationManaged[NCGlobal.shared.configuration_password] as? String, !password.isEmpty, password.lowercased() != "password" {
+ self.configPassword = password
+ }
+ if let apppassword = configurationManaged[NCGlobal.shared.configuration_apppassword] as? String, !apppassword.isEmpty, apppassword.lowercased() != "apppassword" {
+ self.configAppPassword = apppassword
+ }
+ }
+ }
+
+ if (NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig) && accountCount > 0 {
+ navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(self.closeView(sender:)))
+ }
+
+ if accountCount > 0 {
+ navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "users")!.image(color: .label, size: 35), style: .plain, target: self, action: #selector(self.changeUser(sender:)))
+ }
+
+ let config = WKWebViewConfiguration()
+ config.websiteDataStore = WKWebsiteDataStore.nonPersistent()
+
+ webView = WKWebView(frame: CGRect.zero, configuration: config)
+ webView!.navigationDelegate = self
+// webView!.uiDelegate = self
+ view.addSubview(webView!)
+
+ webView!.translatesAutoresizingMaskIntoConstraints = false
+ webView!.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
+ webView!.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
+ webView!.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
+ webView!.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
+
+ // AppConfig
+ if let serverUrl = configServerUrl {
+ if let username = self.configUsername, let password = configAppPassword {
+ self.createAccount(urlBase: serverUrl, user: username, password: password)
+ return
+ } else if let username = self.configUsername, let password = configPassword {
+ getAppPassword(urlBase: serverUrl, user: username, password: password)
+ return
+ } else {
+ urlBase = serverUrl
+ }
+ }
+
+ // ADD end point for Web Flow
+ if urlBase != NCBrandOptions.shared.linkloginPreferredProviders {
+ if loginFlowV2Available {
+ urlBase = loginFlowV2Login
+ } else {
+ urlBase += "/index.php/login/flow"
+ if let user = self.user {
+ urlBase += "?user=\(user)"
+ }
+ }
+ }
+
+ if let url = URL(string: urlBase) {
+ loadWebPage(webView: webView!, url: url)
+ } else {
+ let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_login_url_error_")
+ NCContentPresenter().showError(error: error, priority: .max)
+ }
+
+ if #available(iOS 13, *) {
+ let keyWindow = UIApplication.shared.connectedScenes
+ .filter({$0.activationState == .foregroundActive})
+ .map({$0 as? UIWindowScene})
+ .compactMap({$0})
+ .first?.windows
+ .filter({$0.isKeyWindow}).first
+ let statusBar = UIView(frame: (keyWindow?.windowScene?.statusBarManager?.statusBarFrame)!)
+ statusBar.backgroundColor = NCBrandColor.shared.customer
+ keyWindow?.addSubview(statusBar)
+ } else {
+ if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
+ statusBar.backgroundColor = NCBrandColor.shared.customer
+ }
+ }
+ self.navigationController?.navigationBar.backgroundColor = NCBrandColor.shared.customer
+ self.edgesForExtendedLayout = [] // iphone 16, white gap between navigaiton and status bar.
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ // Re-evaluate in-app messages after viewDidAppear
+ MoEngageAnalytics.shared.displayInAppNotificationSafely(reason: "viewDidAppear")
+
+ // Stop timer error network
+// appDelegate.timerErrorNetworking?.invalidate()
+
+ if let account = NCManageDatabase.shared.getActiveTableAccount(), NCPreferences().getPassword(account: account.account).isEmpty {
+
+ let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
+ let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ present(alertController, animated: true)
+ }
+ }
+
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+
+ NCActivityIndicator.shared.stop()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ // Start timer error network
+// appDelegate.startTimerErrorNetworking()
+ }
+
+ func loadWebPage(webView: WKWebView, url: URL) {
+
+ let language = NSLocale.preferredLanguages[0] as String
+ var request = URLRequest(url: url)
+
+ if let deviceName = "\(UIDevice.current.name) (\(NCBrandOptions.shared.brand) iOS)".cString(using: .utf8),
+ let deviceUserAgent = String(cString: deviceName, encoding: .ascii) {
+ webView.customUserAgent = deviceUserAgent
+ } else {
+ webView.customUserAgent = userAgent
+ }
+
+ request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
+ request.addValue(language, forHTTPHeaderField: "Accept-Language")
+
+ webView.load(request)
+ }
+
+ func getAppPassword(urlBase: String, user: String, password: String) {
+
+ NextcloudKit.shared.getAppPassword(url: urlBase, user: user, password: password) { token, _, error in
+ if error == .success, let password = token {
+ self.createAccount(urlBase: urlBase, user: user, password: password)
+ } else {
+ NCContentPresenter().showError(error: error)
+ self.dismiss(animated: true, completion: nil)
+ }
+ }
+ }
+
+ @objc func closeView(sender: UIBarButtonItem) {
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func changeUser(sender: UIBarButtonItem) {
+// toggleMenu()
+ }
+}
+
+extension NCLoginWeb: WKNavigationDelegate {
+
+ func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
+
+ guard let url = webView.url else { return }
+
+ let urlString: String = url.absoluteString.lowercased()
+
+ // prevent http redirection
+ if urlBase.lowercased().hasPrefix("https://") && urlString.lowercased().hasPrefix("http://") {
+ let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_prevent_http_redirection_", comment: ""), preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
+ _ = self.navigationController?.popViewController(animated: true)
+ }))
+ self.present(alertController, animated: true)
+ return
+ }
+
+ if urlString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol) == true && urlString.contains("login") == true {
+
+ var server: String = ""
+ var user: String = ""
+ var password: String = ""
+
+ let keyValue = url.path.components(separatedBy: "&")
+ for value in keyValue {
+ if value.contains("server:") { server = value }
+ if value.contains("user:") { user = value }
+ if value.contains("password:") { password = value }
+ }
+
+ if !server.isEmpty, !user.isEmpty, !password.isEmpty {
+
+ let server: String = server.replacingOccurrences(of: "/server:", with: "")
+ let username: String = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ")
+ let password: String = password.replacingOccurrences(of: "password:", with: "")
+
+ Task {
+ self.createAccount(urlBase: server, user: username, password: password)
+ }
+ }
+ }
+ }
+
+ func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+ DispatchQueue.global().async {
+ if let serverTrust = challenge.protectionSpace.serverTrust {
+ completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
+ } else {
+ completionHandler(URLSession.AuthChallengeDisposition.useCredential, nil)
+ }
+ }
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ decisionHandler(.allow)
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
+ if let httpResponse = navigationResponse.response as? HTTPURLResponse {
+ print("Response status code: \(httpResponse.statusCode)")
+
+ if httpResponse.statusCode == 403 {
+// showAccessDeniedAlert()
+ closeApp()
+ decisionHandler(.cancel) // Stop loading the page
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+
+ // Show an alert instead of closing the app
+ private func showAccessDeniedAlert() {
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: "Access Denied", message: "You are not authorized to view this page.", preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
+ // Optionally, redirect user
+ self.webView?.load(URLRequest(url: URL(string: "https://www.google.com")!))
+ }))
+ self.present(alert, animated: true, completion: nil)
+ }
+ }
+
+ func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
+ NCActivityIndicator.shared.startActivity(style: .medium, blurEffect: false)
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+
+ NCActivityIndicator.shared.stop()
+
+ if loginFlowV2Available {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+ NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
+ if error == .success && server != nil && loginName != nil && appPassword != nil {
+ self.createAccount(urlBase: server!, user: loginName!, password: appPassword!)
+ }
+ }
+ }
+ }
+ }
+
+ private func closeApp() {
+ // Exit the app immediately
+ exit(0)
+ }
+
+
+ // MARK: -
+
+// func createAccount(urlBase: String, user: String, password: String) {
+// let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+// if let host = URL(string: urlBase)?.host {
+// NCNetworking.shared.writeCertificate(host: host)
+// }
+// NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
+// if error == .success {
+// let window = UIApplication.shared.firstWindow
+// if let controller = window?.rootViewController as? NCMainTabBarController {
+// controller.account = account
+// self.dismiss(animated: true)
+// } else {
+// if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+// AnalyticsHelper.shared.trackUserData()
+// controller.account = account
+// controller.modalPresentationStyle = .fullScreen
+// controller.view.alpha = 0
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+// UIView.animate(withDuration: 0.5) {
+// controller.view.alpha = 1
+// }
+// }
+// }
+// } else {
+// let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+// self.present(alertController, animated: true)
+// }
+// }
+// }
+// func createAccount(urlBase: String, user: String, password: String) {
+// let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+// if let host = URL(string: urlBase)?.host {
+// NCNetworking.shared.writeCertificate(host: host)
+// }
+// NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
+// if error == .success {
+// let window = UIApplication.shared.firstWindow
+// if let controller = window?.rootViewController as? NCMainTabBarController {
+// controller.account = account
+// self.dismiss(animated: true)
+// } else {
+// if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+// AnalyticsHelper.shared.trackUserData()
+// controller.account = account
+// controller.modalPresentationStyle = .fullScreen
+// controller.view.alpha = 0
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+// UIView.animate(withDuration: 0.5) {
+// controller.view.alpha = 1
+// }
+// }
+// }
+// } else {
+// let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+// self.present(alertController, animated: true)
+// }
+// }
+// }
+
+ private func createAccount(urlBase: String, user: String, password: String) {
+ if self.controller == nil {
+ self.controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+ }
+
+ if let host = URL(string: urlBase)?.host {
+ NCNetworking.shared.writeCertificate(host: host)
+ }
+
+ Task {
+ await NCAccount().createAccount(viewController: self, urlBase: urlBase, user: user, password: password, controller: self.controller)
+ }
+ }
+}
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift
index 4a57032ba3..cb72dafccb 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift
@@ -9,36 +9,20 @@ import Alamofire
import LucidBanner
extension NCCollectionViewCommon: UICollectionViewDelegate {
- @MainActor
- func didSelectMetadata(_ metadata: tableMetadata, withOcIds: Bool) async {
- let capabilities = await NKCapabilities.shared.getCapabilities(for: session.account)
-
+ func didSelectMetadata(_ metadata: tableMetadata, withOcIds: Bool) {
+ let capabilities = NCNetworking.shared.capabilities[session.account] ?? NKCapabilities.Capabilities()
if metadata.e2eEncrypted {
if capabilities.e2EEEnabled {
if !NCPreferences().isEndToEndEnabled(account: metadata.account) {
- do {
- let e2ee = NCEndToEndSetup(controller: controller)
- try await e2ee.start()
- } catch let error as NKError {
- if error.errorCode == NSUserCancelledError {
- return
- }
- await showErrorBanner(
- windowScene: windowScene,
- text: error.errorDescription
- )
- return
- } catch {
- // fallback (non NKError)
- await showErrorBanner(
- windowScene: windowScene,
- text: error.localizedDescription
- )
- return
- }
+ let e2ee = NCEndToEndInitialize()
+ e2ee.delegate = self
+ e2ee.initEndToEndEncryption(controller: self.controller, metadata: metadata)
+ return
}
} else {
- await showInfoBanner(windowScene: windowScene, text: "_e2e_server_disabled_")
+ Task {
+ await showInfoBanner(windowScene: windowScene, text: "_e2e_server_disabled_")
+ }
return
}
}
@@ -73,12 +57,11 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
for: token)
}
}
-
if let banner {
await banner.dismissAsync()
}
- if results.nkError == .success || results.nkError == .cancelled {
+ if results.nkError == .success || results.afError?.isExplicitlyCancelledError ?? false {
print("ok")
} else {
await showErrorBanner(windowScene: windowScene, text: results.nkError.errorDescription, errorCode: results.nkError.errorCode)
@@ -86,53 +69,57 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
}
if metadata.directory {
- await pushMetadata(metadata)
+ pushMetadata(metadata)
} else {
- let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: self.global.previewExt1024, userId: metadata.userId, urlBase: metadata.urlBase)
- let fileExists = utilityFileSystem.fileProviderStorageExists(metadata)
-
- // --- E2EE -------
- if metadata.isDirectoryE2EE {
- if fileExists {
- if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: self) {
- self.navigationController?.pushViewController(vc, animated: true)
+ Task { @MainActor in
+ let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: self.global.previewExt1024, userId: metadata.userId, urlBase: metadata.urlBase)
+ let fileExists = utilityFileSystem.fileProviderStorageExists(metadata)
+
+ // --- E2EE -------
+ if metadata.isDirectoryE2EE {
+ if fileExists {
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: self) {
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ } else {
+ await downloadFile()
}
- } else {
- await downloadFile()
+ return
}
- return
- }
- // ---------------
+ // ---------------
- if metadata.isImage || metadata.isAudioOrVideo {
- let metadatas = self.dataSource.getMetadatas()
- let ocIds = metadatas.filter { $0.classFile == NKTypeClassFile.image.rawValue ||
- $0.classFile == NKTypeClassFile.video.rawValue ||
- $0.classFile == NKTypeClassFile.audio.rawValue }.map(\.ocId)
+ if metadata.isImage || metadata.isAudioOrVideo {
+ let metadatas = self.dataSource.getMetadatas()
+ let ocIds = metadatas.filter { $0.classFile == NKTypeClassFile.image.rawValue ||
+ $0.classFile == NKTypeClassFile.video.rawValue ||
+ $0.classFile == NKTypeClassFile.audio.rawValue }.map(\.ocId)
- if let vc = await NCViewer().getViewerController(metadata: metadata, ocIds: withOcIds ? ocIds : nil, image: image, delegate: self) {
- self.navigationController?.pushViewController(vc, animated: true)
- }
- } else if !metadata.isDirectoryE2EE, metadata.isAvailableEditorView || utilityFileSystem.fileProviderStorageExists(metadata) || metadata.name == self.global.talkName {
- if let vc = await NCViewer().getViewerController(metadata: metadata, image: image, delegate: self) {
- self.navigationController?.pushViewController(vc, animated: true)
- }
- } else if NextcloudKit.shared.isNetworkReachable() {
- guard let metadata = await database.setMetadataSessionInWaitDownloadAsync(ocId: metadata.ocId,
- session: self.networking.sessionDownload,
- selector: global.selectorLoadFileView,
- sceneIdentifier: self.controller?.sceneIdentifier) else {
- return
- }
+ if let vc = await NCViewer().getViewerController(metadata: metadata, ocIds: withOcIds ? ocIds : nil, image: image, delegate: self) {
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ } else if !metadata.isDirectoryE2EE, metadata.isAvailableEditorView || utilityFileSystem.fileProviderStorageExists(metadata) || metadata.name == self.global.talkName {
+ if let vc = await NCViewer().getViewerController(metadata: metadata, image: image, delegate: self) {
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ } else if NextcloudKit.shared.isNetworkReachable() {
+ guard let metadata = await database.setMetadataSessionInWaitDownloadAsync(ocId: metadata.ocId,
+ session: self.networking.sessionDownload,
+ selector: global.selectorLoadFileView,
+ sceneIdentifier: self.controller?.sceneIdentifier) else {
+ return
+ }
- if metadata.name == "files" {
- await downloadFile()
- } else if !metadata.url.isEmpty,
- let vc = await NCViewer().getViewerController(metadata: metadata, delegate: self) {
- self.navigationController?.pushViewController(vc, animated: true)
+ if metadata.name == "files" {
+ await downloadFile()
+ } else if !metadata.url.isEmpty,
+ let vc = await NCViewer().getViewerController(metadata: metadata, delegate: self) {
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ } else {
+ Task {
+ await showErrorBanner(windowScene: windowScene, text: "_go_online_", errorCode: NCGlobal.shared.errorOfflineNotAllowed)
+ }
}
- } else {
- await showErrorBanner(windowScene: windowScene, text: "_go_online_", errorCode: NCGlobal.shared.errorOfflineNotAllowed)
}
}
}
@@ -143,20 +130,21 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
}
if self.isEditMode {
- if let index = self.fileSelect.firstIndex(of: metadata.ocId) {
- self.fileSelect.remove(at: index)
- } else {
- self.fileSelect.append(metadata.ocId)
+ if !metadata.e2eEncrypted {
+ if let index = self.fileSelect.firstIndex(of: metadata.ocId) {
+ self.fileSelect.remove(at: index)
+ } else {
+ self.fileSelect.append(metadata.ocId)
+ }
+ self.collectionView.reloadItems(at: [indexPath])
+ self.tabBarSelect?.update(fileSelect: self.fileSelect, metadatas: self.getSelectedMetadatas(), userId: metadata.userId)
+ // self.collectionView.reloadSections(IndexSet(integer: indexPath.section))
}
- self.collectionView.reloadItems(at: [indexPath])
- self.tabBarSelect?.update(fileSelect: self.fileSelect, metadatas: self.getSelectedMetadatas(), userId: metadata.userId)
self.collectionView.collectionViewLayout.invalidateLayout()
return
}
- Task {
- await didSelectMetadata(metadata, withOcIds: true)
- }
+ self.didSelectMetadata(metadata, withOcIds: true)
}
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
@@ -183,7 +171,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
return nil
}, actionProvider: { _ in
- let contextMenu = NCContextMenuMain(metadata: metadata.detachedCopy(), viewController: self, controller: self.controller, sender: cell)
+ let contextMenu = NCContextMenu(metadata: metadata.detachedCopy(), viewController: self, sceneIdentifier: self.sceneIdentifier, sender: cell)
return contextMenu.viewMenu()
})
}
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift
new file mode 100644
index 0000000000..d467e73701
--- /dev/null
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift
@@ -0,0 +1,100 @@
+//
+// NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift
+// Nextcloud
+//
+// Created by Milen on 01.03.24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import Foundation
+import SwipeCellKit
+
+extension NCCollectionViewCommon: SwipeCollectionViewCellDelegate {
+ func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeCellKit.SwipeActionsOrientation) -> [SwipeCellKit.SwipeAction]? {
+ guard orientation == .right, let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return nil }
+
+ let scaleTransition = ScaleTransition(duration: 0.3, initialScale: 0.8, threshold: 0.8)
+
+ // wait a fix for truncate the text .. ? ..
+ let favoriteAction = SwipeAction(style: .default, title: NSLocalizedString(metadata.favorite ? "_favorite_short_" : "_favorite_short_", comment: "") ) { _, _ in
+ NCNetworking.shared.favoriteMetadata(metadata) { error in
+ if error != .success {
+ NCContentPresenter().showError(error: error)
+ }
+ }
+ }
+ favoriteAction.backgroundColor = NCBrandColor.shared.yellowFavorite
+ favoriteAction.image = .init(systemName: "star.fill")
+ favoriteAction.transitionDelegate = scaleTransition
+ favoriteAction.hidesWhenSelected = true
+
+ var actions = [favoriteAction]
+
+ let shareAction = SwipeAction(style: .default, title: NSLocalizedString("_share_", comment: "")) { _, _ in
+ NCActionCenter.shared.openActivityViewController(selectedMetadata: [metadata], controller: self.controller)
+ NCActionCenter.shared.openActivityViewController(selectedMetadata: [metadata])
+ }
+ shareAction.backgroundColor = .blue
+ shareAction.image = .init(systemName: "square.and.arrow.up")
+ shareAction.transitionDelegate = scaleTransition
+ shareAction.hidesWhenSelected = true
+
+ let deleteAction = SwipeAction(style: .destructive, title: NSLocalizedString("_delete_", comment: "")) { _, _ in
+ let titleDelete: String
+
+ if metadata.directory {
+ titleDelete = NSLocalizedString("_delete_folder_", comment: "")
+ } else {
+ titleDelete = NSLocalizedString("_delete_file_", comment: "")
+ }
+
+ let message = NSLocalizedString("_want_delete_", comment: "") + "\n - " + metadata.fileNameView
+
+ let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message, canDeleteServer: !metadata.lock, selectedMetadatas: [metadata], sceneIdentifier: self.controller?.sceneIdentifier) { _ in }
+
+ self.viewController.present(alertController, animated: true, completion: nil)
+ }
+ deleteAction.image = UIImage.init(systemName: "trash")
+ let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message, canDeleteServer: !metadata.lock, selectedMetadatas: [metadata], indexPaths: self.selectIndexPaths) { _ in }
+
+ self.viewController.present(alertController, animated: true, completion: nil)
+ }
+ deleteAction.image = .init(systemName: "trash")
+ deleteAction.style = .destructive
+ deleteAction.transitionDelegate = scaleTransition
+ deleteAction.hidesWhenSelected = true
+
+ if !NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
+ actions.insert(deleteAction, at: 0)
+ }
+
+ if metadata.canShare {
+ actions.append(shareAction)
+ }
+
+ return actions
+ }
+
+ func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
+ var options = SwipeOptions()
+ options.expansionStyle = .selection
+ options.transitionStyle = .border
+ options.backgroundColor = .clear
+ return options
+ }
+}
diff --git a/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
new file mode 100644
index 0000000000..17dbb04c03
--- /dev/null
+++ b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
@@ -0,0 +1,130 @@
+//
+// NCSelectableNavigationView.swift
+// Nextcloud
+//
+// Created by Henrik Storch on 27.01.22.
+// Copyright © 2022 Henrik Storch. All rights reserved.
+//
+// Author Marino Faggiana
+// Author Henrik Storch
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import NextcloudKit
+import Realm
+import UIKit
+
+extension RealmSwiftObject {
+ var primaryKeyValue: String? {
+ guard let primaryKeyName = self.objectSchema.primaryKeyProperty?.name else { return nil }
+ return value(forKey: primaryKeyName) as? String
+ }
+}
+
+public protocol NCSelectableViewTabBar {
+ var tabBarController: UITabBarController? { get }
+ var hostingController: UIViewController? { get }
+}
+
+protocol NCSelectableNavigationView: AnyObject {
+ var viewController: UIViewController { get }
+// var appDelegate: AppDelegate { get }
+ var selectableDataSource: [RealmSwiftObject] { get }
+ var collectionView: UICollectionView! { get set }
+ var isEditMode: Bool { get set }
+ var fileSelect: [String] { get set }
+// var selectIndexPaths: [IndexPath] { get set }
+ var appDelegate: AppDelegate { get }
+ var selectableDataSource: [RealmSwiftObject] { get }
+ var collectionView: UICollectionView! { get set }
+ var isEditMode: Bool { get set }
+ var selectOcId: [String] { get set }
+ var appDelegate: AppDelegate { get }
+ var selectIndexPaths: [IndexPath] { get set }
+ var titleCurrentFolder: String { get }
+ var navigationItem: UINavigationItem { get }
+ var navigationController: UINavigationController? { get }
+ var layoutKey: String { get }
+ var serverUrl: String { get }
+// var tabBarSelect: NCSelectableViewTabBar? { get set }
+// var dataSource: NCCollectionViewDataSource { get set }
+
+// func reloadDataSource(withQueryDB: Bool)
+ var tabBarSelect: NCSelectableViewTabBar? { get set }
+
+ func reloadDataSource(withQueryDB: Bool)
+// func reloadDataSource(withQueryDB: Bool)
+ func setNavigationLeftItems()
+ func setNavigationRightItems(enableMenu: Bool)
+ func createMenuActions() -> [NCMenuAction]
+
+ func toggleSelect(isOn: Bool?)
+ func onListSelected()
+ func onGridSelected()
+}
+
+extension NCSelectableNavigationView {
+ func setNavigationLeftItems() {}
+
+ func saveLayout(_ layoutForView: NCDBLayoutForView) {
+ NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
+ NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
+ setNavigationRightItems(enableMenu: false)
+ }
+
+ /// If explicit `isOn` is not set, it will invert `isEditMode`
+ func toggleSelect(isOn: Bool? = nil) {
+ DispatchQueue.main.async {
+ self.isEditMode = isOn ?? !self.isEditMode
+ self.fileSelect.removeAll()
+// self.selectIndexPaths.removeAll()
+ self.selectOcId.removeAll()
+ self.selectIndexPaths.removeAll()
+ self.setNavigationLeftItems()
+ self.setNavigationRightItems(enableMenu: true)
+ self.collectionView.reloadData()
+ }
+ }
+
+ func collectionViewSelectAll() {
+
+ fileSelect = selectableDataSource.compactMap({ $0.primaryKeyValue })
+// fileSelect = NCCollectionViewDataSource().getMetadataSourceForAllSections().compactMap({ $0.primaryKeyValue })
+// selectOcId = selectableDataSource.compactMap({ $0.primaryKeyValue })
+ collectionView.reloadData()
+ setNavigationRightItems(enableMenu: false)
+ }
+
+ func tapNotification() {
+ if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
+ navigationController?.pushViewController(viewController, animated: true)
+ }
+ }
+
+ func tapTransfer() {
+ if let navController = UIStoryboard(name: "NCTransfers", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+ let viewController = navController.topViewController as? NCTransfers {
+ viewController.modalPresentationStyle = .pageSheet
+// self.present(navigationController, animated: true, completion: nil)
+ navigationController?.present(navController, animated: true, completion: nil)
+ }
+ }
+}
+
+extension NCSelectableNavigationView where Self: UIViewController {
+ var viewController: UIViewController {
+ self
+ }
+}
diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.swift
new file mode 100644
index 0000000000..6cd8ae727d
--- /dev/null
+++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.swift
@@ -0,0 +1,329 @@
+//
+// NCSectionHeaderFooter.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 09/10/2018.
+// Copyright © 2018 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import MarkdownKit
+
+class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate {
+
+ @IBOutlet weak var buttonSwitch: UIButton!
+ @IBOutlet weak var buttonOrder: UIButton!
+ @IBOutlet weak var buttonMore: UIButton!
+ @IBOutlet weak var buttonTransfer: UIButton!
+ @IBOutlet weak var imageButtonTransfer: UIImageView!
+ @IBOutlet weak var labelTransfer: UILabel!
+ @IBOutlet weak var progressTransfer: UIProgressView!
+ @IBOutlet weak var transferSeparatorBottom: UIView!
+ @IBOutlet weak var textViewRichWorkspace: UITextView!
+ @IBOutlet weak var labelSection: UILabel!
+ @IBOutlet weak var viewTransfer: UIView!
+ @IBOutlet weak var viewButtonsView: UIView!
+ @IBOutlet weak var viewSeparator: UIView!
+ @IBOutlet weak var viewRichWorkspace: UIView!
+ @IBOutlet weak var viewSection: UIView!
+
+ @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint!
+ @IBOutlet weak var viewButtonsViewHeightConstraint: NSLayoutConstraint!
+ @IBOutlet weak var viewSeparatorHeightConstraint: NSLayoutConstraint!
+ @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint!
+ @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint!
+ @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint!
+
+ weak var delegate: NCSectionHeaderMenuDelegate?
+ let utility = NCUtility()
+ private var markdownParser = MarkdownParser()
+ private var richWorkspaceText: String?
+ private var textViewColor: UIColor?
+ private let gradient: CAGradientLayer = CAGradientLayer()
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+
+ backgroundColor = .clear
+
+ buttonSwitch.setImage(UIImage(systemName: "list.bullet")!.image(color: NCBrandColor.shared.iconImageColor, size: 25), for: .normal)
+
+ buttonOrder.setTitle("", for: .normal)
+ buttonOrder.setTitleColor(NCBrandColor.shared.brand, for: .normal)
+ buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.iconImageColor, size: 25), for: .normal)
+
+ // Gradient
+ gradient.startPoint = CGPoint(x: 0, y: 0.50)
+ gradient.endPoint = CGPoint(x: 0, y: 1)
+ viewRichWorkspace.layer.addSublayer(gradient)
+
+ let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:)))
+ tap.delegate = self
+ viewRichWorkspace?.addGestureRecognizer(tap)
+
+ viewSeparatorHeightConstraint.constant = 0.5
+ viewSeparator.backgroundColor = .separator
+
+ markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 15), color: .label)
+ markdownParser.header.font = UIFont.systemFont(ofSize: 25)
+ if let richWorkspaceText = richWorkspaceText {
+ textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText)
+ }
+ textViewColor = .label
+
+ labelSection.text = ""
+ viewSectionHeightConstraint.constant = 0
+
+ buttonTransfer.backgroundColor = .clear
+ buttonTransfer.setImage(nil, for: .normal)
+ buttonTransfer.layer.cornerRadius = 6
+ buttonTransfer.layer.masksToBounds = true
+ imageButtonTransfer.image = UIImage(systemName: "stop.circle")
+ imageButtonTransfer.tintColor = .white
+ labelTransfer.text = ""
+ progressTransfer.progress = 0
+ progressTransfer.tintColor = NCBrandColor.shared.brand
+ progressTransfer.trackTintColor = NCBrandColor.shared.brand.withAlphaComponent(0.2)
+ transferSeparatorBottom.backgroundColor = .separator
+ transferSeparatorBottomHeightConstraint.constant = 0.5
+ }
+
+ override func layoutSublayers(of layer: CALayer) {
+ super.layoutSublayers(of: layer)
+
+ gradient.frame = viewRichWorkspace.bounds
+ setInterfaceColor()
+ }
+
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ super.traitCollectionDidChange(previousTraitCollection)
+
+ setInterfaceColor()
+ }
+
+ // MARK: - View
+
+ func setStatusButtonsView(enable: Bool) {
+
+ buttonSwitch.isEnabled = enable
+ buttonOrder.isEnabled = enable
+ buttonMore.isEnabled = enable
+ }
+
+ func buttonMoreIsHidden(_ isHidden: Bool) {
+
+ buttonMore.isHidden = isHidden
+ }
+
+ func setImageSwitchList() {
+
+ buttonSwitch.setImage(UIImage(systemName: "list.bullet")!.image(color: NCBrandColor.shared.iconImageColor, width: 20, height: 15), for: .normal)
+ }
+
+ func setImageSwitchGrid() {
+
+ buttonSwitch.setImage(UIImage(systemName: "square.grid.2x2")!.image(color: NCBrandColor.shared.iconImageColor, size: 20), for: .normal)
+ }
+
+ func setButtonsView(height: CGFloat) {
+
+ viewButtonsViewHeightConstraint.constant = height
+ if height == 0 {
+ viewButtonsView.isHidden = true
+ } else {
+ viewButtonsView.isHidden = false
+ }
+ }
+
+ func setSortedTitle(_ title: String) {
+
+ let title = NSLocalizedString(title, comment: "")
+ buttonOrder.setTitle(title, for: .normal)
+ }
+
+ // MARK: - RichWorkspace
+
+ func setRichWorkspaceHeight(_ size: CGFloat) {
+
+ viewRichWorkspaceHeightConstraint.constant = size
+ if size == 0 {
+ viewRichWorkspace.isHidden = true
+ } else {
+ viewRichWorkspace.isHidden = false
+ }
+ }
+
+ func setInterfaceColor() {
+
+ if traitCollection.userInterfaceStyle == .dark {
+ gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor]
+ } else {
+ gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor]
+ }
+ }
+
+ func setRichWorkspaceText(_ text: String?) {
+ guard let text = text else { return }
+
+ if text != self.richWorkspaceText {
+ textViewRichWorkspace.attributedText = markdownParser.parse(text)
+ self.richWorkspaceText = text
+ }
+ }
+
+ // MARK: - Transfer
+
+ func setViewTransfer(isHidden: Bool, ocId: String? = nil, text: String? = nil, progress: Float? = nil) {
+
+ labelTransfer.text = text
+ viewTransfer.isHidden = isHidden
+ progressTransfer.progress = 0
+
+ if isHidden {
+ viewTransferHeightConstraint.constant = 0
+ } else {
+ var image: UIImage?
+ if let ocId,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+ image = utility.createFilePreviewImage(ocId: metadata.ocId, etag: metadata.etag, fileNameView: metadata.fileNameView, classFile: metadata.classFile, status: metadata.status, createPreviewMedia: true)//?.darken()
+ if image == nil {
+ image = UIImage(named: metadata.iconName)
+ buttonTransfer.backgroundColor = .lightGray
+ } else {
+ buttonTransfer.backgroundColor = .clear
+ }
+ buttonTransfer.setImage(image, for: .normal)
+ }
+ viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer
+ if let progress {
+ progressTransfer.progress = progress
+ }
+ }
+ }
+
+ // MARK: - Section
+
+ func setSectionHeight(_ size: CGFloat) {
+
+ viewSectionHeightConstraint.constant = size
+ if size == 0 {
+ viewSection.isHidden = true
+ } else {
+ viewSection.isHidden = false
+ }
+ }
+
+ // MARK: - Action
+
+ @IBAction func touchUpInsideSwitch(_ sender: Any) {
+ delegate?.tapButtonSwitch(sender)
+ }
+
+ @IBAction func touchUpInsideOrder(_ sender: Any) {
+ delegate?.tapButtonOrder(sender)
+ }
+
+ @IBAction func touchUpInsideMore(_ sender: Any) {
+ delegate?.tapButtonMore(sender)
+ }
+
+ @IBAction func touchUpTransfer(_ sender: Any) {
+ delegate?.tapButtonTransfer(sender)
+ }
+
+ @objc func touchUpInsideViewRichWorkspace(_ sender: Any) {
+ delegate?.tapRichWorkspace(sender)
+ }
+}
+
+protocol NCSectionHeaderMenuDelegate: AnyObject {
+ func tapButtonSwitch(_ sender: Any)
+ func tapButtonOrder(_ sender: Any)
+ func tapButtonMore(_ sender: Any)
+ func tapButtonTransfer(_ sender: Any)
+ func tapRichWorkspace(_ sender: Any)
+}
+
+// optional func
+extension NCSectionHeaderMenuDelegate {
+ func tapButtonSwitch(_ sender: Any) {}
+ func tapButtonOrder(_ sender: Any) {}
+ func tapButtonMore(_ sender: Any) {}
+ func tapButtonTransfer(_ sender: Any) {}
+ func tapRichWorkspace(_ sender: Any) {}
+}
+
+// optional func
+extension NCSectionFooterDelegate {
+ func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {}
+}
+
+//// https://stackoverflow.com/questions/16278463/darken-an-uiimage
+//public extension UIImage {
+//
+// private enum BlendMode {
+// case multiply // This results in colors that are at least as dark as either of the two contributing sample colors
+// case screen // This results in colors that are at least as light as either of the two contributing sample colors
+// }
+//
+// // A level of zero yeilds the original image, a level of 1 results in black
+// func darken(level: CGFloat = 0.5) -> UIImage? {
+// return blend(mode: .multiply, level: level)
+// }
+//
+// // A level of zero yeilds the original image, a level of 1 results in white
+// func lighten(level: CGFloat = 0.5) -> UIImage? {
+// return blend(mode: .screen, level: level)
+// }
+//
+// private func blend(mode: BlendMode, level: CGFloat) -> UIImage? {
+// let context = CIContext(options: nil)
+//
+// var level = level
+// if level < 0 {
+// level = 0
+// } else if level > 1 {
+// level = 1
+// }
+//
+// let filterName: String
+// switch mode {
+// case .multiply: // As the level increases we get less white
+// level = abs(level - 1.0)
+// filterName = "CIMultiplyBlendMode"
+// case .screen: // As the level increases we get more white
+// filterName = "CIScreenBlendMode"
+// }
+//
+// let blender = CIFilter(name: filterName)!
+// let backgroundColor = CIColor(color: UIColor(white: level, alpha: 1))
+//
+// guard let inputImage = CIImage(image: self) else { return nil }
+// blender.setValue(inputImage, forKey: kCIInputImageKey)
+//
+// guard let backgroundImageGenerator = CIFilter(name: "CIConstantColorGenerator") else { return nil }
+// backgroundImageGenerator.setValue(backgroundColor, forKey: kCIInputColorKey)
+// guard let backgroundImage = backgroundImageGenerator.outputImage?.cropped(to: CGRect(origin: CGPoint.zero, size: self.size)) else { return nil }
+// blender.setValue(backgroundImage, forKey: kCIInputBackgroundImageKey)
+//
+// guard let blendedImage = blender.outputImage else { return nil }
+//
+// guard let cgImage = context.createCGImage(blendedImage, from: blendedImage.extent) else { return nil }
+// return UIImage(cgImage: cgImage)
+// }
+//}
diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.xib
new file mode 100644
index 0000000000..5d206c1d2f
--- /dev/null
+++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.xib
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create/NCCreateDocument.swift b/iOSClient/Main/Create/NCCreateDocument.swift
new file mode 100644
index 0000000000..6cc78c4669
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateDocument.swift
@@ -0,0 +1,200 @@
+//
+// NCCreateDocument.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 22/06/24.
+// Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import Foundation
+import UIKit
+import NextcloudKit
+
+class NCCreateDocument: NSObject {
+ let utility = NCUtility()
+ let database = NCManageDatabase.shared
+ let global = NCGlobal.shared
+// var fileNameExtension = ""
+
+ @MainActor
+ func createDocument(controller: NCMainTabBarController, fileNamePath: String, fileName: String, fileNameExtension: String = "", editorId: String, creatorId: String? = nil, templateId: String, account: String) async {
+ let session = NCSession.shared.getSession(account: account)
+ guard let viewController = controller.currentViewController() else {
+ return
+ }
+ var UUID = NSUUID().uuidString
+ UUID = "TEMP" + UUID.replacingOccurrences(of: "-", with: "")
+ var options = NKRequestOptions()
+ let serverUrl = controller.currentServerUrl()
+
+ if let creatorId, editorId == "text" || editorId == "onlyoffice" {
+ if editorId == "onlyoffice" {
+ options = NKRequestOptions(customUserAgent: NCUtility().getCustomUserAgentOnlyOffice())
+ } else if editorId == "text" {
+ options = NKRequestOptions(customUserAgent: NCUtility().getCustomUserAgentNCText())
+ }
+ let results = await NextcloudKit.shared.textCreateFileAsync(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateId, account: account, options: options) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: account,
+ path: fileNamePath,
+ name: "textCreateFile")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+
+ var result = await NKTypeIdentifiers.shared.getInternalType(fileName: fileName, mimeType: "", directory: false, account: session.account)
+
+ controller.dismiss(animated: true, completion: {
+
+ Task {
+ let metadata = await NCManageDatabaseCreateMetadata().createMetadataAsync(
+ fileName: fileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: viewController) {
+ viewController.navigationController?.pushViewController(vc, animated: true)
+ }
+ }
+ })
+
+ } else if editorId == "collabora" {
+
+ let results = await NextcloudKit.shared.createRichdocumentsAsync(path: fileNamePath, templateId: templateId, account: account) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: account,
+ path: fileNamePath,
+ name: "CreateRichdocuments")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+
+ controller.dismiss(animated: true, completion: {
+
+ Task {
+ let createFileName = (fileName as NSString).deletingPathExtension + "." + fileNameExtension
+ let metadata = await NCManageDatabaseCreateMetadata().createMetadataAsync(
+ fileName: createFileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+ AnalyticsHelper.shared.trackCreateFile(metadata: metadata)
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: viewController) {
+ viewController.navigationController?.pushViewController(vc, animated: true)
+ }
+ }
+ })
+
+ }
+ }
+
+ func getTemplate(editorId: String, templateId: String, account: String) async -> (templates: [NKEditorTemplate], selectedTemplate: NKEditorTemplate, ext: String) {
+ var templates: [NKEditorTemplate] = []
+ var selectedTemplate = NKEditorTemplate()
+ var ext: String = ""
+
+ if editorId == "text" || editorId == "onlyoffice" {
+ var options = NKRequestOptions()
+ if editorId == "onlyoffice" {
+ options = NKRequestOptions(customUserAgent: NCUtility().getCustomUserAgentOnlyOffice())
+ } else if editorId == "text" {
+ options = NKRequestOptions(customUserAgent: NCUtility().getCustomUserAgentNCText())
+ }
+
+ let results = await NextcloudKit.shared.textGetListOfTemplatesAsync(account: account, options: options) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: account,
+ name: "textGetListOfTemplates")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ if results.error == .success, let resultTemplates = results.templates {
+ for template in resultTemplates {
+ var temp = NKEditorTemplate()
+ temp.identifier = template.identifier
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+ templates.append(temp)
+ // default: template empty
+ if temp.preview.isEmpty {
+ selectedTemplate = temp
+ ext = template.ext
+ }
+ }
+ }
+
+ if templates.isEmpty {
+ var temp = NKEditorTemplate()
+ temp.identifier = ""
+ if editorId == "text" {
+ temp.ext = "md"
+ } else if editorId == "onlyoffice" && templateId == "document" {
+ temp.ext = "docx"
+ } else if editorId == "onlyoffice" && templateId == "spreadsheet" {
+ temp.ext = "xlsx"
+ } else if editorId == "onlyoffice" && templateId == "presentation" {
+ temp.ext = "pptx"
+ }
+ temp.name = "Empty"
+ temp.preview = ""
+ templates.append(temp)
+ selectedTemplate = temp
+ ext = temp.ext
+ }
+ }
+
+ if editorId == "collabora" {
+ let results = await NextcloudKit.shared.getTemplatesRichdocumentsAsync(typeTemplate: templateId, account: account) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: account,
+ path: templateId,
+ name: "getTemplatesRichdocuments")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ if results.error == .success {
+ for template in results.templates! {
+ var temp = NKEditorTemplate()
+ temp.identifier = "\(template.templateId)"
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+ templates.append(temp)
+ // default: template empty
+ if temp.preview.isEmpty {
+ selectedTemplate = temp
+ ext = temp.ext
+ }
+ }
+ }
+ }
+
+ return (templates, selectedTemplate, ext)
+ }
+}
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard b/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard
new file mode 100644
index 0000000000..6995ac52d9
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.storyboard
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
new file mode 100644
index 0000000000..99c1194d9b
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadDocuments.swift
@@ -0,0 +1,597 @@
+//
+// NCCreateFormUploadDocuments.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 14/11/18.
+// Copyright © 2018 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import NextcloudKit
+import XLForm
+
+// MARK: -
+
+@objc class NCCreateFormUploadDocuments: XLFormViewController, NCSelectDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, NCCreateFormUploadConflictDelegate {
+
+ @IBOutlet weak var indicator: UIActivityIndicatorView!
+ @IBOutlet weak var collectionView: UICollectionView!
+ @IBOutlet weak var collectionViewHeigth: NSLayoutConstraint!
+
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ var editorId = ""
+ var creatorId = ""
+ var typeTemplate = ""
+ var templateIdentifier = ""
+ var serverUrl = ""
+ var fileNameFolder = ""
+ var fileName = ""
+ var fileNameExtension = ""
+ var titleForm = ""
+ var listOfTemplate: [NKEditorTemplate] = []
+ var selectTemplate: NKEditorTemplate?
+ let utilityFileSystem = NCUtilityFileSystem()
+ let utility = NCUtility()
+
+ // Layout
+ let numItems = 2
+ let sectionInsets: CGFloat = 10
+ let highLabelName: CGFloat = 20
+
+ var controller: NCMainTabBarController!
+ var session: NCSession.Session {
+ NCSession.shared.getSession(controller: controller)
+ }
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ fileNameFolder = "/"
+ } else {
+ fileNameFolder = utilityFileSystem.getTextServerUrl(session: session, serverUrl: serverUrl)//(serverUrl as NSString).lastPathComponent
+ }
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+ view.backgroundColor = .systemGroupedBackground
+ collectionView.backgroundColor = .systemGroupedBackground
+ tableView.backgroundColor = .secondarySystemGroupedBackground
+
+ let cancelButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
+ let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
+ cancelButton.tintColor = NCBrandColor.shared.brand
+ saveButton.tintColor = NCBrandColor.shared.brand
+
+ self.navigationItem.leftBarButtonItem = cancelButton
+ self.navigationItem.rightBarButtonItem = saveButton
+ self.navigationItem.rightBarButtonItem?.isEnabled = false
+
+ // title
+ self.title = titleForm
+
+ fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
+
+ initializeForm()
+ getTemplate()
+ }
+
+ // MARK: - Tableview (XLForm)
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: "")
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder")!.withTintColor(NCBrandColor.shared.customer)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
+ if(self.fileNameFolder == "/"){
+ row.cellConfig["photoLabel.text"] = NSLocalizedString("_prefix_upload_path_", comment: "")
+ }else{
+ row.cellConfig["photoLabel.text"] = self.fileNameFolder
+ }
+ row.cellConfig["textLabel.text"] = ""
+
+ section.addFormRow(row)
+
+ // Section: File Name
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: "").uppercased())
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kMyAppCustomCellType"] = NCCreateDocumentCustomTextField.self
+
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "kMyAppCustomCellType", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = NCCreateDocumentCustomTextField.self
+
+ row.cellConfigAtConfigure["backgroundColor"] = UIColor.secondarySystemGroupedBackground;
+ row.cellConfig["fileNameTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameTextField.textColor"] = UIColor.label
+ row.cellConfig["fileNameTextField.placeholder"] = self.fileName
+
+ section.addFormRow(row)
+
+ self.form = form
+ // tableView.reloadData()
+ // collectionView.reloadData()
+ }
+
+ override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = tableView.backgroundColor
+ }
+
+ override func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = tableView.backgroundColor
+ }
+
+ // MARK: - CollectionView
+
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return listOfTemplate.count
+ }
+
+ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+
+ let itemWidth: CGFloat = (collectionView.frame.width - (sectionInsets * 4) - CGFloat(numItems)) / CGFloat(numItems)
+ let itemHeight: CGFloat = itemWidth + highLabelName
+
+ collectionViewHeigth.constant = itemHeight + sectionInsets
+
+ return CGSize(width: itemWidth, height: itemHeight)
+ }
+
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
+
+ let template = listOfTemplate[indexPath.row]
+
+ // image
+ let imagePreview = cell.viewWithTag(100) as? UIImageView
+ if !template.preview.isEmpty {
+ let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + template.name + ".png"
+ if FileManager.default.fileExists(atPath: fileNameLocalPath) {
+ let imageURL = URL(fileURLWithPath: fileNameLocalPath)
+ if let image = UIImage(contentsOfFile: imageURL.path) {
+ imagePreview?.image = image
+ }
+ } else {
+ getImageFromTemplate(name: template.name, preview: template.preview, indexPath: indexPath)
+ }
+ }
+
+ // name
+ let name = cell.viewWithTag(200) as? UILabel
+ name?.text = template.name
+ name?.textColor = .secondarySystemGroupedBackground
+
+ // select
+ let imageSelect = cell.viewWithTag(300) as? UIImageView
+ if selectTemplate != nil && selectTemplate?.name == template.name {
+ cell.backgroundColor = .label
+ imageSelect?.image = UIImage(named: "plus100")
+ imageSelect?.isHidden = false
+ } else {
+ cell.backgroundColor = .secondarySystemGroupedBackground
+ imageSelect?.isHidden = true
+ }
+
+ return cell
+ }
+
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+
+ let template = listOfTemplate[indexPath.row]
+
+ selectTemplate = template
+ fileNameExtension = template.ext
+
+ collectionView.reloadData()
+ }
+
+ // MARK: - Action
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool, session: NCSession.Session) {
+
+ guard let serverUrl = serverUrl else { return }
+
+ self.serverUrl = serverUrl
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ fileNameFolder = "/"
+ } else {
+ fileNameFolder = (serverUrl as NSString).lastPathComponent
+ }
+
+ let buttonDestinationFolder: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
+ buttonDestinationFolder.cellConfig["photoLabel.text"] = fileNameFolder
+
+ self.tableView.reloadData()
+ }
+
+// override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+// super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+//// if formRow.tag == "fileName" {
+//// self.form.delegate = nil
+//// if let fileNameNew = formRow.value {
+//// self.fileName = CCUtility.removeForbiddenCharactersServer(fileNameNew as? String)
+//// }
+//// formRow.value = self.fileName
+//// self.form.delegate = self
+//// }
+// }
+
+ @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
+
+ self.deselectFormRow(sender)
+
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+ let viewController = navigationController.topViewController as? NCSelect {
+
+ viewController.delegate = self
+ viewController.typeOfCommandView = .selectCreateFolder
+
+ self.present(navigationController, animated: true, completion: nil)
+ }
+ }
+
+ @objc func save() {
+
+ Task {
+ guard let selectTemplate = self.selectTemplate else { return }
+ templateIdentifier = selectTemplate.identifier
+
+ let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ var fileName = rowFileName.value as? String
+ if fileName?.isEmpty ?? false || fileName == nil {
+ fileName = NCUtilityFileSystem().createFileNameDate("Text", ext: getFileExtension())
+ } else if fileName?.trimmingCharacters(in: .whitespaces).isEmpty ?? false {
+ let alert = UIAlertController(title: "", message: NSLocalizedString("_please_enter_file_name_", comment: ""), preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
+ self.present(alert, animated: true)
+ return
+ }
+
+ // Ensure fileName is not nil or empty
+ guard var fileNameForm: String = fileName, !fileNameForm.isEmpty else { return }
+
+ // Trim whitespaces and newlines
+ fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
+
+// let fileAutoRenamer = FileAutoRenamer()
+ let session = NCSession.shared.getSession(controller: self.controller)
+ let capabilities = await NKCapabilities.shared.getCapabilities(for: session.account)
+ fileName = FileAutoRenamer.rename(fileNameForm, isFolderPath: true, capabilities: capabilities)
+
+ let result = await NKTypeIdentifiers.shared.getInternalType(fileName: fileNameForm, mimeType: "", directory: false, account: session.account)
+
+ if utility.editorsDirectEditing(account: session.account, contentType: result.mimeType).isEmpty {
+ fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
+ }
+
+ // verify if already exists
+ if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", session.account, self.serverUrl, fileNameForm)) != nil {
+ NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
+ return
+ // }
+ //
+ // if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm), nativeFormat: false) != nil {
+ //
+ // let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate.sceneIdentifier)
+ //
+ // guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+ //
+ // conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+ // conflict.alwaysNewFileNameNumber = true
+ // conflict.serverUrl = serverUrl
+ // conflict.metadatasUploadInConflict = [metadataForUpload]
+ // conflict.delegate = self
+ //
+ // self.present(conflict, animated: true, completion: nil)
+
+ } else {
+
+ let fileNamePath = utilityFileSystem.getRelativeFilePath(String(describing: fileNameForm), serverUrl: serverUrl, session: session)
+ await NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileNameForm), fileNameExtension: self.fileNameExtension, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account)
+
+ }
+ }
+ }
+
+ func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+
+ if let metadatas, metadatas.count > 0 {
+ let fileName = metadatas[0].fileName
+ let fileNamePath = utilityFileSystem.getRelativeFilePath(fileName, serverUrl: serverUrl, session: session)
+// createDocument(fileNamePath: fileNamePath, fileName: fileName)
+ Task {
+ await NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileName), editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account)
+ }
+
+ } else {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.cancel()
+ }
+ }
+ }
+
+ func createDocument(fileNamePath: String, fileName: String) {
+
+ self.navigationItem.rightBarButtonItem?.isEnabled = false
+ var UUID = NSUUID().uuidString
+ UUID = "TEMP" + UUID.replacingOccurrences(of: "-", with: "")
+
+ if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
+
+ Task {
+ var options = NKRequestOptions()
+ if self.editorId == NCGlobal.shared.editorOnlyoffice {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
+ } else if editorId == NCGlobal.shared.editorText {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
+ }
+
+ let results = await NextcloudKit.shared.textCreateFileAsync(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, account: session.account, options: options) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: self.session.account,
+ path: fileNamePath,
+ name: "textCreateFile")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+ let metadata = await NCManageDatabaseCreateMetadata().createMetadataAsync(
+ fileName: fileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+
+ AnalyticsHelper.shared.trackCreateFile(metadata: metadata)
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: controller) {
+ controller.navigationController?.pushViewController(vc, animated: true)
+ }
+ }
+ }
+
+ if self.editorId == NCGlobal.shared.editorCollabora {
+ Task {
+
+ let results = await NextcloudKit.shared.createRichdocumentsAsync(path: fileNamePath, templateId: templateIdentifier, account: session.account) { task in
+ Task {
+ let identifier = await NCNetworking.shared.networkingTasks.createIdentifier(account: self.session.account,
+ path: fileNamePath,
+ name: "CreateRichdocuments")
+ await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task)
+ }
+ }
+ guard results.error == .success, let url = results.url else {
+ return NCContentPresenter().showError(error: results.error)
+ }
+
+ let metadata = await NCManageDatabaseCreateMetadata().createMetadataAsync(
+ fileName: fileName,
+ ocId: UUID,
+ serverUrl: serverUrl,
+ url: url,
+ session: session,
+ sceneIdentifier: controller.sceneIdentifier)
+
+ if let vc = await NCViewer().getViewerController(metadata: metadata, delegate: controller) {
+ controller.navigationController?.pushViewController(vc, animated: true)
+ }
+ }
+ }
+ }
+
+ @objc func cancel() {
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ // MARK: NC API
+
+ func getTemplate() {
+
+ indicator.color = NCBrandColor.shared.brandElement
+ indicator.startAnimating()
+
+ if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
+
+ var options = NKRequestOptions()
+ if self.editorId == NCGlobal.shared.editorOnlyoffice {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentOnlyOffice())
+ } else if editorId == NCGlobal.shared.editorText {
+ options = NKRequestOptions(customUserAgent: utility.getCustomUserAgentNCText())
+ }
+
+ NextcloudKit.shared.textGetListOfTemplates(account: session.account, options: options) { account, templates, _, error in
+
+ self.indicator.stopAnimating()
+
+ if error == .success && account == self.session.account, let templates = templates {
+
+ for template in templates {
+
+ var temp = NKEditorTemplate()
+
+ temp.identifier = template.identifier
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+
+ self.listOfTemplate.append(temp)
+
+ // default: template empty
+ if temp.preview.isEmpty {
+ self.selectTemplate = temp
+ self.fileNameExtension = template.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+ }
+ }
+
+ if self.listOfTemplate.isEmpty {
+
+ var temp = NKEditorTemplate()
+
+ temp.identifier = ""
+ if self.editorId == NCGlobal.shared.editorText {
+ temp.ext = "md"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templateDocument {
+ temp.ext = "docx"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templateSpreadsheet {
+ temp.ext = "xlsx"
+ } else if self.editorId == NCGlobal.shared.editorOnlyoffice && self.typeTemplate == NCGlobal.shared.templatePresentation {
+ temp.ext = "pptx"
+ }
+ temp.name = "Empty"
+ temp.preview = ""
+
+ self.listOfTemplate.append(temp)
+
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+
+ self.collectionView.reloadData()
+ }
+
+ }
+
+ if self.editorId == NCGlobal.shared.editorCollabora {
+
+ NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate, account: session.account) { account, templates, _, error in
+
+ self.indicator.stopAnimating()
+
+ if error == .success && account == self.session.account {
+
+ for template in templates! {
+
+ var temp = NKEditorTemplate()
+
+ temp.identifier = "\(template.templateId)"
+// temp.delete = template.delete
+ temp.ext = template.ext
+ temp.name = template.name
+ temp.preview = template.preview
+// temp.type = template.type
+
+ self.listOfTemplate.append(temp)
+
+ // default: template empty
+ if temp.preview.isEmpty {
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+ }
+ }
+
+ if self.listOfTemplate.isEmpty {
+
+ var temp = NKEditorTemplate()
+
+ temp.identifier = ""
+ if self.typeTemplate == NCGlobal.shared.templateDocument {
+ temp.ext = "docx"
+ } else if self.typeTemplate == NCGlobal.shared.templateSpreadsheet {
+ temp.ext = "xlsx"
+ } else if self.typeTemplate == NCGlobal.shared.templatePresentation {
+ temp.ext = "pptx"
+ }
+ temp.name = "Empty"
+ temp.preview = ""
+
+ self.listOfTemplate.append(temp)
+
+ self.selectTemplate = temp
+ self.fileNameExtension = temp.ext
+ self.navigationItem.rightBarButtonItem?.isEnabled = true
+ }
+
+ self.collectionView.reloadData()
+ }
+ }
+ }
+
+ func getImageFromTemplate(name: String, preview: String, indexPath: IndexPath) {
+
+ let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + name + ".png"
+
+ NextcloudKit.shared.download(serverUrlFileName: preview, fileNameLocalPath: fileNameLocalPath, account: session.account, requestHandler: { _ in
+
+ }, taskHandler: { _ in
+
+ }, progressHandler: { _ in
+
+ }) { account, _, _, _, _, _, error in
+
+ if error == .success && account == self.session.account {
+ self.collectionView.reloadItems(at: [indexPath])
+ } else if error != .success {
+ print("\(error.errorCode)")
+ } else {
+ print("[ERROR] It has been changed user during networking process, error.")
+ }
+ }
+ }
+
+ func getFileExtension() -> String {
+ switch typeTemplate {
+ case NCGlobal.shared.editorText:
+ return "md"
+ case NCGlobal.shared.templateDocument:
+ return "docx"
+ case NCGlobal.shared.templateSpreadsheet:
+ return "xlsx"
+ case NCGlobal.shared.templatePresentation:
+ return "pptx"
+ default:
+ return ""
+ }
+ }
+}
diff --git a/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.storyboard b/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.storyboard
new file mode 100644
index 0000000000..1b5344ccee
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.storyboard
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.swift
new file mode 100644
index 0000000000..63bdea4f27
--- /dev/null
+++ b/iOSClient/Main/Create/NCCreateFormUploadVoiceNote.swift
@@ -0,0 +1,388 @@
+//
+// NCCreateFormUploadVoiceNote.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 9/03/2019.
+// Copyright © 2019 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import NextcloudKit
+import XLForm
+import AVFAudio
+
+class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAudioPlayerDelegate, NCCreateFormUploadConflictDelegate {
+
+ @IBOutlet weak var buttonPlayStop: UIButton!
+ @IBOutlet weak var labelTimer: UILabel!
+ @IBOutlet weak var labelDuration: UILabel!
+ @IBOutlet weak var progressView: UIProgressView!
+
+ private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let utilityFileSystem = NCUtilityFileSystem()
+ let utility = NCUtility()
+ private var serverUrl = ""
+ private var titleServerUrl = ""
+ private var fileName = ""
+ private var fileNamePath = ""
+ private var durationPlayer: TimeInterval = 0
+ private var counterSecondPlayer: TimeInterval = 0
+
+ private var audioPlayer: AVAudioPlayer!
+ private var timer = Timer()
+
+ var cellBackgoundColor = UIColor.secondarySystemGroupedBackground
+
+ var controller: NCMainTabBarController!
+ var session: NCSession.Session {
+ NCSession.shared.getSession(controller: controller)
+ }
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ self.navigationItem.rightBarButtonItem?.tintColor = NCBrandColor.shared.brand
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+ view.backgroundColor = .systemGroupedBackground
+ tableView.backgroundColor = .systemGroupedBackground
+ cellBackgoundColor = .secondarySystemGroupedBackground
+
+ self.title = NSLocalizedString("_voice_memo_title_", comment: "")
+
+ // Button Play Stop
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: NCBrandColor.shared.iconImageColor, size: 100), for: .normal)
+
+ // Progress view
+ progressView.progress = 0
+ progressView.progressTintColor = NCBrandColor.shared.customer
+ progressView.trackTintColor = .white
+ progressView.layer.borderWidth = 1
+ progressView.layer.cornerRadius = 5.0
+ progressView.layer.borderColor = NCBrandColor.shared.customer.cgColor
+
+ labelTimer.textColor = .label
+ labelDuration.textColor = .label
+
+ initializeForm()
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ updateTimerUI()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ if audioPlayer.isPlaying {
+ stop()
+ }
+ }
+
+ public func setup(serverUrl: String, fileNamePath: String, fileName: String) {
+
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ titleServerUrl = "/"
+ } else {
+ titleServerUrl = utilityFileSystem.getTextServerUrl(session: session, serverUrl: serverUrl)//(serverUrl as NSString).lastPathComponent
+ }
+
+ self.fileName = fileName
+ self.serverUrl = serverUrl
+ self.fileNamePath = fileNamePath
+
+ // player
+ do {
+ try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileNamePath))
+ audioPlayer.prepareToPlay()
+ audioPlayer.delegate = self
+ durationPlayer = TimeInterval(audioPlayer.duration)
+ } catch {
+ buttonPlayStop.isEnabled = false
+ }
+ }
+
+ // MARK: XLForm
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: "").uppercased())
+ section.footerTitle = ""
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: self.titleServerUrl)
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder_nmcloud")?.image(color: NCBrandColor.shared.brandElement, size: 25)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.right.rawValue
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
+ if(self.titleServerUrl == "/"){
+ row.cellConfig["photoLabel.text"] = NSLocalizedString("_prefix_upload_path_", comment: "")
+ }else{
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ }
+ row.cellConfig["textLabel.text"] = ""
+ section.addFormRow(row)
+
+ // Section: File Name
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kMyAppCustomCellType"] = TextTableViewCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: "").uppercased())
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "kMyAppCustomCellType", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = TextTableViewCell.self
+ row.cellConfigAtConfigure["backgroundColor"] = UIColor.secondarySystemGroupedBackground;
+ row.cellConfig["fileNameTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameTextField.textColor"] = UIColor.label
+ row.cellConfig["fileNameTextField.text"] = self.fileName
+ section.addFormRow(row)
+
+ self.form = form
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "fileName" {
+
+ self.form.delegate = nil
+
+ if let fileNameNew = formRow.value as? String {
+ Task {
+ let capabilities = await NKCapabilities.shared.getCapabilities(for: session.account)
+ self.fileName = FileAutoRenamer.rename(fileNameNew, isFolderPath: true, capabilities: capabilities)
+ }
+ } else {
+ self.fileName = ""
+ }
+
+
+ self.form.delegate = self
+ }
+ }
+
+ // MARK: TableViewDelegate
+
+ override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = cellBackgoundColor
+ }
+
+ // MARK: - Action
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool, session: NCSession.Session) {
+
+ if serverUrl != nil {
+
+ self.serverUrl = serverUrl!
+
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ self.titleServerUrl = "/"
+ } else {
+ self.titleServerUrl = (serverUrl! as NSString).lastPathComponent
+ }
+
+ // Update
+ let row: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ self.updateFormRow(row)
+ }
+ }
+
+ @objc func save() {
+ let name = self.fileName
+ guard name.trimmingCharacters(in: .whitespaces) != "" else {
+ let alert = UIAlertController(title: "", message: NSLocalizedString("_prompt_insert_file_name", comment: ""), preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
+ self.present(alert, animated: true)
+ return
+ }
+ let ext = (name as NSString).pathExtension.uppercased()
+ var fileNameSave = ""
+
+ if ext.isEmpty {
+ fileNameSave = name + ".m4a"
+ } else {
+ fileNameSave = (name as NSString).deletingPathExtension + ".m4a"
+ }
+
+ Task {
+ let fileNamePath = NSTemporaryDirectory() + fileNameSave
+ let metadata = await NCManageDatabaseCreateMetadata().createMetadataAsync(
+ fileName: fileNameSave,
+ ocId: UUID().uuidString,
+ serverUrl: serverUrl,//controller.currentServerUrl(),
+ session: self.session,
+ sceneIdentifier: self.controller?.sceneIdentifier)
+
+ metadata.session = NCNetworking.shared.sessionUploadBackground
+ metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
+ metadata.status = NCGlobal.shared.metadataStatusWaitUpload
+ metadata.sessionDate = Date()
+ metadata.size = NCUtilityFileSystem().getFileSize(filePath: fileNamePath)
+
+ if NCManageDatabase.shared.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: fileNameSave, nativeFormat: false) != nil {
+
+ guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+
+ conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+ conflict.serverUrl = serverUrl
+ conflict.metadatasUploadInConflict = [metadata]
+ conflict.delegate = self
+
+ self.present(conflict, animated: true, completion: nil)
+
+ } else {
+
+ dismissAndUpload(metadata)
+ }
+ }
+ }
+
+ func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+
+ if let metadatas, metadatas.count > 0 {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.dismissAndUpload(metadatas[0])
+ }
+ }
+ }
+
+ func dismissAndUpload(_ metadata: tableMetadata) {
+
+ AnalyticsHelper.shared.trackCreateVoiceMemo(size: metadata.size, date: metadata.creationDate as Date)
+
+ NCUtilityFileSystem().copyFile(atPath: fileNamePath, toPath: NCUtilityFileSystem().getDirectoryProviderStorageOcId(metadata.ocId,
+ fileName: metadata.fileNameView,
+ userId: metadata.userId,
+ urlBase: metadata.urlBase))
+
+ Task {
+ await NCManageDatabase.shared.addMetadataAsync(metadata)
+ }
+ NCNetworkingProcess.shared.createProcessUploads(metadatas: [metadata], completion: { _ in })
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func cancel() {
+
+ try? FileManager.default.removeItem(atPath: fileNamePath)
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
+
+ self.deselectFormRow(sender)
+
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+ let viewController = navigationController.topViewController as? NCSelect {
+
+ viewController.delegate = self
+ viewController.typeOfCommandView = .selectCreateFolder
+ viewController.includeDirectoryE2EEncryption = true
+
+ self.present(navigationController, animated: true, completion: nil)
+ }
+ }
+
+ // MARK: Player - Timer
+
+ func updateTimerUI() {
+ labelTimer.text = String().formatSecondsToString(counterSecondPlayer)
+ labelDuration.text = String().formatSecondsToString(durationPlayer)
+ progressView.progress = Float(counterSecondPlayer / durationPlayer)
+ }
+
+ @objc func updateTimer() {
+ counterSecondPlayer += 1
+ updateTimerUI()
+ }
+
+ @IBAction func playStop(_ sender: Any) {
+
+ if audioPlayer.isPlaying {
+
+ stop()
+
+ } else {
+
+ start()
+ }
+ }
+
+ func start() {
+
+ audioPlayer.prepareToPlay()
+ audioPlayer.play()
+
+ timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
+
+ buttonPlayStop.setImage(UIImage(named: "stop")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+
+ func stop() {
+
+ audioPlayer.currentTime = 0.0
+ audioPlayer.stop()
+
+ timer.invalidate()
+ counterSecondPlayer = 0
+ progressView.progress = 0
+ updateTimerUI()
+
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+
+ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+
+ timer.invalidate()
+ counterSecondPlayer = 0
+ progressView.progress = 0
+ updateTimerUI()
+
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+}
diff --git a/iOSClient/Main/Create/TextTableViewCell.swift b/iOSClient/Main/Create/TextTableViewCell.swift
new file mode 100644
index 0000000000..8777b1878a
--- /dev/null
+++ b/iOSClient/Main/Create/TextTableViewCell.swift
@@ -0,0 +1,95 @@
+//
+// TextTableViewCell.swift
+// Nextcloud
+//
+// Created by Ashu on 23/04/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+class TextTableViewCell: XLFormBaseCell, UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameTextField: UITextField!
+ @IBOutlet weak var topLineView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+ fileNameTextField.delegate = self
+ topLineView.backgroundColor = UIColor.secondarySystemBackground
+
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ if (rowDescriptor.tag == "maskFileName"){
+ topLineView.isHidden = true
+ }else{
+ topLineView.isHidden = false
+ }
+
+ fileNameTextField.tintColor = UIColor.systemGray
+ fileNameTextField.selectedTextRange = fileNameTextField.textRange(from: fileNameTextField.beginningOfDocument, to: fileNameTextField.endOfDocument)
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameTextField == textField {
+ if let rowDescriptor = rowDescriptor, let text = self.fileNameTextField.text {
+
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameTextField)
+ rowDescriptor.value = nil
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: NSRange.init().self, replacementString: "")
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
+
+extension UITextField {
+ @IBInspectable var placeholderColor: UIColor {
+ get {
+ return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
+ }
+ set {
+ guard let attributedPlaceholder = attributedPlaceholder else { return }
+ let attributes: [NSAttributedString.Key: UIColor] = [.foregroundColor: newValue]
+ self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
+ }
+ }
+}
diff --git a/iOSClient/Main/Create/TextTableViewCell.xib b/iOSClient/Main/Create/TextTableViewCell.xib
new file mode 100644
index 0000000000..e4be37d230
--- /dev/null
+++ b/iOSClient/Main/Create/TextTableViewCell.xib
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Media/NCMediaCommandView.xib b/iOSClient/Media/NCMediaCommandView.xib
new file mode 100644
index 0000000000..b2df2d68eb
--- /dev/null
+++ b/iOSClient/Media/NCMediaCommandView.xib
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.swift b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
new file mode 100644
index 0000000000..cb7553eda7
--- /dev/null
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.swift
@@ -0,0 +1,34 @@
+//
+// FolderPathCustomCell.swift
+// Nextcloud
+//
+// Created by A200073704 on 04/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+class FolderPathCustomCell: XLFormButtonCell{
+
+ @IBOutlet weak var photoLabel: UILabel!
+ @IBOutlet weak var folderImage: UIImageView!
+ @IBOutlet weak var bottomLineView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ if (rowDescriptor.tag == "PhotoButtonDestinationFolder"){
+ bottomLineView.isHidden = true
+ }else{
+ bottomLineView.isHidden = false
+ }
+ }
+}
diff --git a/iOSClient/NMC Custom Views/FolderPathCustomCell.xib b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
new file mode 100644
index 0000000000..caca063abf
--- /dev/null
+++ b/iOSClient/NMC Custom Views/FolderPathCustomCell.xib
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
new file mode 100644
index 0000000000..3983e413a6
--- /dev/null
+++ b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.swift
@@ -0,0 +1,71 @@
+//
+// NCCreateDocumentCustomTextField.swift
+// Nextcloud
+//
+// Created by A200073704 on 04/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+class NCCreateDocumentCustomTextField: XLFormBaseCell,UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameTextField: UITextField!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+ fileNameTextField.delegate = self
+
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameTextField == textField {
+ if let rowDescriptor = rowDescriptor {
+ if let text = textField.text{
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameTextField)
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
diff --git a/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib
new file mode 100644
index 0000000000..3fd86bb2d6
--- /dev/null
+++ b/iOSClient/NMC Custom Views/NCCreateDocumentCustomTextField.xib
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NCCreateFormUploadScanDocument.swift b/iOSClient/Scan document/NCCreateFormUploadScanDocument.swift
new file mode 100644
index 0000000000..bc2e39f8b2
--- /dev/null
+++ b/iOSClient/Scan document/NCCreateFormUploadScanDocument.swift
@@ -0,0 +1,1334 @@
+//
+// NCCreateFormUploadScanDocument.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 14/11/2018.
+// Copyright © 2018 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import NextcloudKit
+import Vision
+import VisionKit
+import Photos
+import XLForm
+
+class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NCCreateFormUploadConflictDelegate {
+
+ let appDelegate = UIApplication.shared.delegate as? AppDelegate
+
+ enum TypeQuality {
+ case low
+ case medium
+ case high
+ }
+ var quality: TypeQuality = .medium
+
+ var editorId = ""
+ var serverUrl = ""
+ var titleServerUrl = ""
+ var arrayImages: [UIImage] = []
+ var fileName = NCUtilityFileSystem().createFileNameDate("scan", ext: "pdf")
+ var password: String = ""
+ var fileType = "PDF"
+ var isPDFWithOCRSwitchOn = false
+ var isPDFWithoutOCRSwitchOn = false
+ var isSetpasswordEnable = false
+
+ var isTextFileSwitchOn = false
+ var isPNGFormatSwitchOn = false
+ var isJPGFormatSwitchOn = false
+ var isOCRActivatedFileConflicts = false
+
+
+ var cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+ let utilityFileSystem = NCUtilityFileSystem()
+ let utility = NCUtility()
+ let contentPresenter = NCContentPresenter()
+ var controller: NCMainTabBarController!
+ var session: NCSession.Session {
+ NCSession.shared.getSession(controller: controller)
+ }
+
+ // MARK: - View Life Cycle
+
+ convenience init(serverUrl: String, arrayImages: [UIImage]) {
+
+ self.init()
+
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate?.urlBase ?? "", userId: appDelegate?.userId ?? "") {
+ titleServerUrl = "/"
+ } else {
+ titleServerUrl = (serverUrl as NSString).lastPathComponent
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ titleServerUrl = "/"
+ } else {
+ titleServerUrl = utilityFileSystem.getTextServerUrl(session: session, serverUrl: serverUrl)//(serverUrl as NSString).lastPathComponent
+ }
+
+ self.serverUrl = serverUrl
+ self.arrayImages = arrayImages
+ }
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+
+ super.viewDidLoad()
+
+ self.title = NSLocalizedString("_save_settings_", comment: "")
+
+ let saveButton : UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
+
+ self.navigationItem.rightBarButtonItem = saveButton
+ let cancelButton : UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
+ self.navigationItem.leftBarButtonItem = cancelButton
+ self.navigationItem.rightBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+ changeTheming()
+
+ initializeForm()
+
+ let value = NCKeychain().textRecognitionStatus
+ setTextRecognition(newValue: value)
+ NotificationCenter.default.addObserver(self, selector: #selector(appTerminateNotify), name: UIApplication.willTerminateNotification, object: nil)
+ }
+
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ super.traitCollectionDidChange(previousTraitCollection)
+
+ changeTheming()
+ }
+
+ override func viewWillDisappear(_ animated: Bool) {
+ NotificationCenter.default.removeObserver(self)
+ showDeleteAlert()
+ }
+
+ @objc func appTerminateNotify() {
+ showDeleteAlert()
+ }
+ // MARK: - Theming
+
+ @objc func changeTheming() {
+
+ view.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+ tableView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+ cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+ tableView.reloadData()
+ }
+
+ // MARK: XLForm
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: ""))
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCScamFileNameCustomInputField"] = FileNameInputTextField.self
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "NMCScamFileNameCustomInputField", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = FileNameInputTextField.self
+ row.cellConfig["fileNameInputTextField.placeholder"] = self.fileName
+
+ row.cellConfig["fileNameInputTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameInputTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameInputTextField.textColor"] = NCBrandColor.shared.label
+
+
+ section.addFormRow(row)
+ //FileName custom view END
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_location_", comment: ""))
+ form.addFormSection(section)
+
+ //Scan documnet folder path
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCScanFolderPathCustomCell"] = ScanDocumentPathView.self
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "NMCScanFolderPathCustomCell", title: self.titleServerUrl)
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["backgroundColor"] = cellBackgoundColor
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder")?.imageColor(NCBrandColor.shared.customer)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = NCBrandColor.shared.label
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: "").uppercased())
+ section.footerTitle = ""
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: self.titleServerUrl)
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder_nmcloud")?.image(color: NCBrandColor.shared.brandElement, size: 25)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
+ if(self.titleServerUrl == "/"){
+ row.cellConfig["photoLabel.text"] = NSLocalizedString("_prefix_upload_path_", comment: "")
+ }else{
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ }
+ row.cellConfig["textLabel.text"] = ""
+
+ section.addFormRow(row)
+ // END of Scan documnet folder path
+
+ section.addFormRow(row)
+
+ // Section: File Name
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kMyAppCustomCellType"] = TextTableViewCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: "").uppercased())
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "kMyAppCustomCellType", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = TextTableViewCell.self
+ row.cellConfigAtConfigure["backgroundColor"] = UIColor.secondarySystemGroupedBackground;
+ row.cellConfig["fileNameTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameTextField.textColor"] = UIColor.label
+ row.cellConfig["fileNameTextField.text"] = self.fileName
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_with_text_recognition_", comment: ""))
+ form.addFormSection(section)
+
+ // Save with Text Recognition PDF
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCPdfWithOCRSwitchCell"] = PdfWithOcrSwitchView.self
+ row = XLFormRowDescriptor(tag: "PDFWithOCRSwitch", rowType: "NMCPdfWithOCRSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_pdf_with_ocr_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ // END of Save with Text Recognition PDF
+
+ //Save with Text Recognition text file
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCTextFileWithOCRSwitchCell"] = TextFileWithOcrSwitchView.self
+ row = XLFormRowDescriptor(tag: "TextFileWithOCRSwitch", rowType: "NMCTextFileWithOCRSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_text_file_ocr_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ //END of Save with Text Recognition text file
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_without_text_recognition_", comment: ""))
+ form.addFormSection(section)
+
+ // PDF without text recongnition
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCPDFWithoutOCRSwitchCell"] = PdfWithoutOcrSwitchView.self
+ row = XLFormRowDescriptor(tag: "PDFWithoutOCRSwitch", rowType: "NMCPDFWithoutOCRSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_pdf_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ // END of PDF without text recongnition
+
+ // JPG without text recongnition
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCJPGWithoutOCRSwitchCell"] = JPGImageSaveSwitchView.self
+ row = XLFormRowDescriptor(tag: "JPGWithoutOCRSwitch", rowType: "NMCJPGWithoutOCRSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_jpg_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ // END JPG without text recongnition
+
+ // PNG without text recongnition
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCPNGWithoutOCRSwitchCell"] = PNGImageSaveSwitchView.self
+ row = XLFormRowDescriptor(tag: "PNGWithoutOCRSwitch", rowType: "NMCPNGWithoutOCRSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_png_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ // END without text recongnition
+
+ // PDF password section
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_pdf_password_", comment: ""))
+ form.addFormSection(section)
+ // END of PDF password
+
+ // Set PDF password switch
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCSetPDFPasswordSwitchCell"] = SetPDFPasswordSwitchView.self
+ row = XLFormRowDescriptor(tag: "PDFSetPasswordSwitch", rowType: "NMCSetPDFPasswordSwitchCell", title: self.titleServerUrl)
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_set_password_", comment: "")
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label
+
+ section.addFormRow(row)
+ // END of set PDF password switch
+
+ // enter password input field
+ XLFormViewController.cellClassesForRowDescriptorTypes()["NMCSetPasswordCustomInputField"] = PasswordInputField.self
+ row = XLFormRowDescriptor(tag: "SetPasswordInputField", rowType: "NMCSetPasswordCustomInputField", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = PasswordInputField.self
+ row.cellConfig["fileNameInputTextField.placeholder"] = NSLocalizedString("_password_", comment: "")
+
+ row.cellConfig["fileNameInputTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameInputTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameInputTextField.textColor"] = NCBrandColor.shared.label
+ row.cellConfig["backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground
+ row.hidden = 1
+
+
+ section.addFormRow(row)
+
+ // Section: Password
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_pdf_password_", comment: ""))
+ // form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "password", rowType: XLFormRowDescriptorTypePassword, title: NSLocalizedString("_password_", comment: ""))
+ row.cellConfig["backgroundColor"] = NCBrandColor.shared.backgroundForm
+
+ row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textLabel.textColor"] = NCBrandColor.shared.label
+
+ row.cellConfig["textField.textAlignment"] = NSTextAlignment.right.rawValue
+ row.cellConfig["textField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textField.textColor"] = NCBrandColor.shared.label
+
+ self.form = form
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "textRecognition" {
+
+ }
+
+ if formRow.tag == "fileName" {
+
+ self.form.delegate = nil
+
+ let fileNameNew = newValue as? String
+
+ if let fileNameNew = newValue as? String {
+ self.fileName = utility.removeForbiddenCharacters(fileNameNew)
+ self.fileName = FileAutoRenamer.rename(filename: fileNameNew, isFolderPath: true)
+ } else {
+ self.fileName = ""
+ }
+
+ formRow.value = self.fileName
+
+ self.updateFormRow(formRow)
+
+ self.form.delegate = self
+ }
+
+ if formRow.tag == "password" {
+ let stringPassword = newValue as? String
+ if stringPassword != nil {
+ password = stringPassword!
+ } else {
+ password = ""
+ }
+ }
+
+ if formRow.tag == "filetype" {
+ fileType = newValue as? String ?? ""
+
+ let rowFileName : XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ let rowPassword : XLFormRowDescriptor = self.form.formRow(withTag: "password")!
+ rowFileName.value = createFileName(rowFileName.value as? String)
+
+ self.updateFormRow(rowFileName)
+
+ // rowPassword
+ if fileType == "JPG" || fileType == "TXT" {
+ rowPassword.value = ""
+ password = ""
+ rowPassword.disabled = true
+ } else {
+ rowPassword.disabled = false
+ }
+
+ self.updateFormRow(rowPassword)
+ }
+
+ if formRow.tag == "PDFSetPasswordSwitch"{
+
+ isSetpasswordEnable = (formRow.value! as AnyObject).boolValue
+ let setPasswordInputField : XLFormRowDescriptor = self.form.formRow(withTag: "SetPasswordInputField")!
+ if (formRow.value! as AnyObject).boolValue == true {
+ setPasswordInputField.hidden = 0
+ }else {
+ setPasswordInputField.hidden = 1
+ }
+ }
+
+ if formRow.tag == "PDFWithOCRSwitch"{
+ //TODO
+ print("In PDF with OCR: value for without OCR \(isPDFWithoutOCRSwitchOn)")
+ isPDFWithOCRSwitchOn = (formRow.value! as AnyObject).boolValue
+ if (!isPDFWithoutOCRSwitchOn){
+ if (formRow.value! as AnyObject).boolValue == true {
+ let setPasswordSwitchOption : XLFormRowDescriptor = self.form.formRow(withTag: "PDFSetPasswordSwitch")!
+ setPasswordSwitchOption.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label//isEnabled
+ setPasswordSwitchOption.value = "enable_switch"
+
+ self.tableView.reloadData()
+ }else{
+ let setPasswordSwitchOption : XLFormRowDescriptor = self.form.formRow(withTag: "PDFSetPasswordSwitch")!
+ setPasswordSwitchOption.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.graySoft
+ setPasswordSwitchOption.disabled = true
+ setPasswordSwitchOption.value = "disable_switch"
+ self.tableView.reloadData()
+
+ }
+ }
+
+ }
+
+ if formRow.tag == "PDFWithoutOCRSwitch"{
+ print("In PDF without OCR: value for with OCR \(isPDFWithOCRSwitchOn)")
+
+ isPDFWithoutOCRSwitchOn = (formRow.value! as AnyObject).boolValue
+ if(!isPDFWithOCRSwitchOn){
+ if (formRow.value! as AnyObject).boolValue == true {
+ let setPasswordSwitchOption : XLFormRowDescriptor = self.form.formRow(withTag: "PDFSetPasswordSwitch")!
+ setPasswordSwitchOption.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.label//isEnabled
+ setPasswordSwitchOption.value = "enable_switch"
+
+ self.tableView.reloadData()
+ }else{
+ let setPasswordSwitchOption : XLFormRowDescriptor = self.form.formRow(withTag: "PDFSetPasswordSwitch")!
+ setPasswordSwitchOption.cellConfig["cellLabel.textColor"] = NCBrandColor.shared.graySoft
+ setPasswordSwitchOption.disabled = true
+ setPasswordSwitchOption.value = "disable_switch"
+ self.tableView.reloadData()
+
+ }
+ }
+
+ }
+
+ if formRow.tag == "SetPasswordInputField" {
+ let stringPassword = newValue as? String
+ if stringPassword != nil {
+ password = stringPassword!
+ } else {
+ password = ""
+ }
+ }
+
+ if formRow.tag == "TextFileWithOCRSwitch" {
+ isTextFileSwitchOn = (formRow.value! as AnyObject).boolValue
+ self.setTextRecognition(newValue: newValue as? Bool ?? false)
+ }
+ if formRow.tag == "JPGWithoutOCRSwitch" {
+ isJPGFormatSwitchOn = (formRow.value! as AnyObject).boolValue
+ }
+ if formRow.tag == "PNGWithoutOCRSwitch" {
+ isPNGFormatSwitchOn = (formRow.value! as AnyObject).boolValue
+ }
+
+
+ }
+
+
+ override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
+ return 4
+ }
+ func setTextRecognition(newValue: Bool) {
+
+ let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+
+ self.form.delegate = nil
+ fileType = "PDF"
+ rowFileName.value = createFileName(rowFileName.value as? String)
+ self.updateFormRow(rowFileName)
+ self.tableView.reloadData()
+
+ NCKeychain().textRecognitionStatus = newValue
+
+ self.form.delegate = self
+ }
+
+ func createFileName(_ fileName: String?) -> String {
+
+ var name: String = ""
+ var newFileName: String = ""
+
+ if fileName == nil || fileName == "" {
+ name = utilityFileSystem.createFileNameDate("scan", ext: "pdf")
+ name = utilityFileSystem.createFileNameDate("scan", ext: "pdf")
+ } else {
+ name = fileName!
+ }
+
+ let ext = (name as NSString).pathExtension.uppercased()
+
+ if ext == "" {
+ newFileName = name + "." + fileType.lowercased()
+ } else {
+ newFileName = (name as NSString).deletingPathExtension + "." + fileType.lowercased()
+ }
+
+ return newFileName
+ }
+
+ // MARK: - Action
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+
+ if serverUrl != nil {
+
+ self.serverUrl = serverUrl!
+
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate?.urlBase ?? "", userId: appDelegate?.userId ?? "") {
+ if serverUrl == utilityFileSystem.getHomeServer(session: session) {
+ self.titleServerUrl = "/"
+ } else {
+ self.titleServerUrl = (serverUrl! as NSString).lastPathComponent
+ }
+
+ // Update
+ let row : XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
+ row.title = self.titleServerUrl
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ self.updateFormRow(row)
+ }
+ }
+
+ func startProcessForSaving(){
+
+ let rowFileName : XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ guard let name = rowFileName.value else {
+ return
+ }
+ if name as? String == "" {
+ return
+ }
+
+ let ext = (name as? NSString)?.pathExtension.uppercased()
+ var fileNameSave = ""
+
+ if ext == "" {
+ fileNameSave = (name as? String ?? "") + "." + fileType.lowercased()
+ } else {
+ fileNameSave = ((name as? NSString)?.deletingPathExtension ?? "") + "." + fileType.lowercased()
+ }
+
+
+ NCActivityIndicator.shared.startActivity(backgroundView: self.view, style: .medium)
+
+ saveImages(fileNameSave: fileNameSave,fileType: "jpg",metadataConflict: nil,completion: { (success) -> Void in
+ print("jpg line of code executed")
+ if success { // this will be equal to whatever value is set in this method call
+ print("true")
+ saveImages(fileNameSave: fileNameSave,fileType: "png",metadataConflict: nil,completion: { (success) -> Void in
+ print("png line of code executed")
+ if success {
+ if(isPDFWithOCRSwitchOn){
+ savePDF(ocrSwitchOn: true,completion: {(success) -> Void in
+ if success{
+ if(isPDFWithoutOCRSwitchOn){
+ savePDF(ocrSwitchOn: false, completion: {(success) -> Void in
+ if success{
+ //Save Text File
+ if(isTextFileSwitchOn){
+ saveTxtFile(completion: {(success) -> Void in
+ if success{
+ //DELETE Files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ })
+ }else{
+ //DELETE files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ }
+ })
+ }else{
+ //Save Text File
+ if(isTextFileSwitchOn){
+ saveTxtFile(completion: {(success) -> Void in
+ if success{
+ //DELETE Files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ })
+ }else{
+ //DELETE files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ }
+ }
+ })
+ }else {
+ if(isPDFWithoutOCRSwitchOn){
+ savePDF(ocrSwitchOn: false, completion: {(success) -> Void in
+ if success{
+ //Save Text File
+ if(isTextFileSwitchOn){
+ saveTxtFile(completion: {(success) -> Void in
+ if success{
+ //DELETE Files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ })
+ }else{
+ //DELETE files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ }
+ })
+ }else{
+ //Save Text File
+ if(isTextFileSwitchOn){
+ saveTxtFile(completion: {(success) -> Void in
+ if success{
+ //DELETE Files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ })
+ }else{
+ //DELETE files
+ showDeleteAlert()
+ NCActivityIndicator.shared.stop()
+ }
+ }
+ }
+ }
+ })
+ }
+ })
+ }
+
+
+ @objc func save() {
+ let password = (getPasswordFromField() ?? "").trimmingCharacters(in: .whitespaces)
+ self.password = password
+
+ if(isSetpasswordEnable && password == ""){
+ showAlert()
+ return
+ }
+
+ if(!isAtleastOneFiletypeSelected()){
+
+ let alertController = UIAlertController(title: "", message: NSLocalizedString("_no_file_type_selection_error_", comment: ""), preferredStyle: .alert)
+ let alertWindow = UIWindow(frame: UIScreen.main.bounds)
+ alertWindow.windowLevel = UIWindow.Level.alert
+ alertWindow.rootViewController = UIViewController()
+ alertWindow.makeKeyAndVisible()
+ let actionOk = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { (action:UIAlertAction) in
+ alertController.dismiss(animated: true, completion: nil)
+ }
+
+ alertController.addAction(actionOk)
+ self.present(alertController, animated: true)
+ }else {
+ // Request delete all image scanned
+ let alertController = UIAlertController(title: "", message: NSLocalizedString("_saved_info_alert_", comment: ""), preferredStyle: .alert)
+ let alertWindow = UIWindow(frame: UIScreen.main.bounds)
+ alertWindow.windowLevel = UIWindow.Level.alert
+ alertWindow.rootViewController = UIViewController()
+ alertWindow.makeKeyAndVisible()
+ let actionOk = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { (action:UIAlertAction) in
+ self.startProcessForSaving()
+ alertController.dismiss(animated: true, completion: nil)
+ }
+
+ alertController.addAction(actionOk)
+ self.present(alertController, animated: true)
+ }
+
+ }
+
+ func showAlert(){
+ // Request delete all image scanned
+ let alertController = UIAlertController(title: "", message: NSLocalizedString("_no_password_warn_", comment: ""), preferredStyle: .alert)
+ let alertWindow = UIWindow(frame: UIScreen.main.bounds)
+ alertWindow.windowLevel = UIWindow.Level.alert
+ alertWindow.rootViewController = UIViewController()
+ alertWindow.makeKeyAndVisible()
+ let actionOk = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { (action:UIAlertAction) in
+ alertController.dismiss(animated: true, completion: nil)
+ }
+
+ alertController.addAction(actionOk)
+ //alertWindow.addSubview(alertController)
+ self.present(alertController, animated: true)
+ //alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
+
+ }
+
+ func showAlertForInfo(){
+ // Request delete all image scanned
+ let alertController = UIAlertController(title: "", message: NSLocalizedString("_saved_info_alert_", comment: ""), preferredStyle: .alert)
+ let alertWindow = UIWindow(frame: UIScreen.main.bounds)
+ alertWindow.windowLevel = UIWindow.Level.alert
+ alertWindow.rootViewController = UIViewController()
+ alertWindow.makeKeyAndVisible()
+ let actionOk = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { (action:UIAlertAction) in
+ alertController.dismiss(animated: true, completion: nil)
+ }
+
+ alertController.addAction(actionOk)
+ //alertWindow.addSubview(alertController)
+ self.present(alertController, animated: true)
+ }
+ func saveTxtFile(completion: (Bool) -> ()){
+
+ let rowFileName : XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ guard var name = rowFileName.value else {
+ return
+ }
+ if name as? String == "" {
+ return
+ }
+
+ for count in 0.. ()){
+
+ let rowFileName : XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ guard var name = rowFileName.value else {
+ return
+ }
+ if name as? String == "" {
+ return
+ }
+
+ if (ocrSwitchOn){
+ name = name as? String
+ }
+
+ let ext = (name as? NSString)?.pathExtension.uppercased()
+ var fileNameSave = ""
+
+ if (ext == "") {
+ if (ocrSwitchOn){
+ fileNameSave = (name as? String ?? "") + "_OCR" + "." + "pdf"
+ }else{
+ fileNameSave = (name as? String ?? "") + "." + "pdf"
+ }
+ } else {
+ if(ocrSwitchOn){
+ fileNameSave = ((name as? NSString)?.deletingPathExtension ?? "") + "_OCR" + "." + fileType.lowercased()
+
+ }else{
+ fileNameSave = ((name as? NSString)?.deletingPathExtension ?? "") + "." + fileType.lowercased()
+ }
+ }
+
+
+ let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate?.account ?? "", user: appDelegate?.user ?? "", userId: appDelegate?.userId ?? "", fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate?.urlBase ?? "", url: "", contentType: "")
+
+// let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate?.account ?? "", user: appDelegate?.user ?? "", userId: appDelegate?.userId ?? "", fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate?.urlBase ?? "", url: "", contentType: "")
+ let metadataForUpload = NCManageDatabase.shared.createMetadata(fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, url: "", contentType: "", session: session, sceneIdentifier: self.appDelegate?.sceneIdentifier)
+
+ metadataForUpload.session = NCNetworking.shared.sessionUploadBackground
+ metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
+ metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
+
+ NCActivityIndicator.shared.startActivity(backgroundView: self.view, style: .medium)
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.isPDFWithOCRSwitchOn = ocrSwitchOn
+ self.dismissAndUpload(metadataForUpload, fileType: "PDF")
+ }
+
+ completion(true)
+ }
+
+ fileprivate func showDeleteAlert() {
+
+ let path = utilityFileSystem.directoryScan
+
+ do {
+ let filePaths = try FileManager.default.contentsOfDirectory(atPath: path)
+ for filePath in filePaths {
+ try FileManager.default.removeItem(atPath: path + "/" + filePath)
+ }
+ } catch let error as NSError {
+ print("Error: \(error.debugDescription)")
+ }
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ func saveImages(fileNameSave: String, fileType: String, metadataConflict: tableMetadata?, completion: (Bool) -> ()) {
+
+ if(!isJPGFormatSwitchOn && fileType == "jpg"){
+ completion(true)
+ return
+ }else if(!isPNGFormatSwitchOn && fileType == "png"){
+ completion(true)
+ return
+ }
+ else {
+ let rowFileName : XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ guard var name = rowFileName.value else {
+ return
+ }
+ if name as? String == "" {
+ return
+ }
+
+ for count in 0.. 0 {
+ NCActivityIndicator.shared.startActivity(backgroundView: self.view, style: .medium)
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ // if let metadata = metadatas![0] as? tableMetadata{
+ if (metadatas![0].fileExtension == "jpg"){
+ //TODO for jpg
+ self.dismissAndUpload(metadatas![0],fileType: "JPG")
+ NCActivityIndicator.shared.stop()
+
+ }else if(metadatas![0].fileExtension == "png") {
+ //TODO for png
+ self.dismissAndUpload(metadatas![0],fileType: "PNG")
+ NCActivityIndicator.shared.stop()
+
+
+ }else if(metadatas![0].fileExtension == "pdf"){
+ //TODO for pdf
+ self.isOCRActivatedFileConflicts = metadatas![0].fileName.contains("_OCR")
+ self.dismissAndUpload(metadatas![0],fileType: "PDF")
+ NCActivityIndicator.shared.stop()
+
+ }else if(metadatas![0].fileExtension == "txt"){
+ //TODO for txt
+ self.dismissAndUpload(metadatas![0],fileType: "TXT")
+ NCActivityIndicator.shared.stop()
+
+ }
+ }
+ }
+ }
+
+ func dismissAndUpload(_ metadata: tableMetadata, fileType: String?) {
+
+ let fileNameGenerateExport = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)
+ let fileUrl = URL(fileURLWithPath: fileNameGenerateExport)
+
+ // Text Recognition TXT
+ // if fileType == "TXT" && self.form.formRow(withTag: "textRecognition")!.value as! Int == 1 {
+ if fileType == "TXT" {
+
+ var textFile = ""
+ for image in self.arrayImages {
+
+ let requestHandler = VNImageRequestHandler(cgImage: image.cgImage!, options: [:])
+
+ let request = VNRecognizeTextRequest { request, _ in
+ guard let observations = request.results as? [VNRecognizedTextObservation] else {
+ NCActivityIndicator.shared.stop()
+ return
+ }
+ for observation in observations {
+ guard let textLine = observation.topCandidates(1).first else {
+ continue
+ }
+
+ textFile += textLine.string
+ textFile += "\n"
+ }
+ }
+
+ request.recognitionLevel = .accurate
+ request.usesLanguageCorrection = true
+ try? requestHandler.perform([request])
+ }
+
+ do {
+
+ try textFile.write(to: fileUrl, atomically: true, encoding: .utf8)
+ } catch {
+ NCActivityIndicator.shared.stop()
+ let error = NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: "_error_creation_file_")
+ contentPresenter.showError(error: error)
+ return
+ }
+ }
+
+ if fileType == "PDF" {
+
+ let pdfData = NSMutableData()
+
+ if !password.isEmpty {
+ for char in password.unicodeScalars {
+ if !char.isASCII {
+ NCActivityIndicator.shared.stop()
+ let error = NKError(errorCode: NCGlobal.shared.errorForbidden, errorDescription: "_password_ascii_")
+ contentPresenter.showError(error: error)
+ return
+ }
+ }
+ let info: [AnyHashable: Any] = [kCGPDFContextUserPassword as String: password, kCGPDFContextOwnerPassword as String: password]
+ UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, info)
+ } else {
+ UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
+ }
+ var fontColor = UIColor.clear
+#if targetEnvironment(simulator)
+ fontColor = UIColor.red
+#endif
+
+ for var image in self.arrayImages {
+
+ image = changeCompressionImage(image)
+
+ let bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
+
+ //self.form.formRow(withTag: "textRecognition")!.value as! Int == 1
+ if isPDFWithOCRSwitchOn {
+
+ UIGraphicsBeginPDFPageWithInfo(bounds, nil)
+ image.draw(in: bounds)
+
+ let requestHandler = VNImageRequestHandler(cgImage: image.cgImage!, options: [:])
+
+ let request = VNRecognizeTextRequest { request, _ in
+ guard let observations = request.results as? [VNRecognizedTextObservation] else {
+ NCActivityIndicator.shared.stop()
+ return
+ }
+ for observation in observations {
+ guard let textLine = observation.topCandidates(1).first else {
+ continue
+ }
+
+ var t: CGAffineTransform = CGAffineTransform.identity
+ t = t.scaledBy(x: image.size.width, y: -image.size.height)
+ t = t.translatedBy(x: 0, y: -1)
+ let rect = observation.boundingBox.applying(t)
+ let text = textLine.string
+
+ let font = UIFont.systemFont(ofSize: rect.size.height, weight: .regular)
+ let attributes = self.bestFittingFont(for: text, in: rect, fontDescriptor: font.fontDescriptor, fontColor: fontColor)
+
+ text.draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
+ }
+ }
+
+ request.recognitionLevel = .accurate
+ request.usesLanguageCorrection = true
+ try? requestHandler.perform([request])
+
+ } else {
+
+ UIGraphicsBeginPDFPageWithInfo(bounds, nil)
+ image.draw(in: bounds)
+ }
+ }
+
+ UIGraphicsEndPDFContext()
+
+ do {
+ try pdfData.write(to: fileUrl, options: .atomic)
+ } catch {
+ print("error catched")
+ }
+ }
+
+ if fileType == "JPG" {
+
+ //let image = changeCompressionImage(self.arrayImages[0])
+
+ for image in self.arrayImages {
+ // let image = changeCompressionImage(self.arrayImages[0])
+ guard let data = image.jpegData(compressionQuality: CGFloat(0.5)) else {
+ NCActivityIndicator.shared.stop()
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: "_error_creation_file_"), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: NSLocalizedString("_error_creation_file_", comment: "")), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ return
+ }
+
+ do {
+ try data.write(to: fileUrl, options: .atomic)
+ } catch {
+ NCActivityIndicator.shared.stop()
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: "_error_creation_file_"), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: NSLocalizedString("_error_creation_file_", comment: "")), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ return
+ }
+ }
+
+
+ }
+
+ if fileType == "PNG" {
+
+ //let image = changeCompressionImage(self.arrayImages[0])
+
+ for image in self.arrayImages {
+ // let image = changeCompressionImage(self.arrayImages[0])
+ guard let data = image.jpegData(compressionQuality: CGFloat(0.5)) else {
+ NCActivityIndicator.shared.stop()
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: "_error_creation_file_"), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: NSLocalizedString("_error_creation_file_", comment: "")), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ return
+ }
+
+ do {
+ try data.write(to: NSURL.fileURL(withPath: fileNameGenerateExport), options: .atomic)
+ } catch {
+ NCActivityIndicator.shared.stop()
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: "_error_creation_file_"), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ contentPresenter.messageNotification("_error_", error: NKError(errorCode: NCGlobal.shared.errorCreationFile, errorDescription: NSLocalizedString("_error_creation_file_", comment: "")), delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info)
+ return
+ }
+ }
+ }
+
+ metadata.size = utilityFileSystem.getFileSize(filePath: fileNameGenerateExport)
+
+ NCActivityIndicator.shared.stop()
+
+ NCNetworkingProcess.shared.createProcessUploads(metadatas: [metadata], completion: { _ in })
+
+ // Request delete all image scanned
+ let alertController = UIAlertController(title: "", message: NSLocalizedString("_delete_all_scanned_images_", comment: ""), preferredStyle: .alert)
+
+ let actionYes = UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { [self] (_: UIAlertAction) in
+
+ let path = utilityFileSystem.directoryScan
+
+ do {
+ let filePaths = try FileManager.default.contentsOfDirectory(atPath: path)
+ for filePath in filePaths {
+ try FileManager.default.removeItem(atPath: path + "/" + filePath)
+ }
+ } catch let error as NSError {
+ print("Error: \(error.debugDescription)")
+ }
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ let actionNo = UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ alertController.addAction(actionYes)
+ alertController.addAction(actionNo)
+ self.present(alertController, animated: true, completion: nil)
+ }
+
+
+ @objc func cancel() {
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
+
+ self.deselectFormRow(sender)
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController {
+ let viewController = navigationController.topViewController as? NCSelect
+ viewController?.delegate = self
+ viewController?.typeOfCommandView = .selectCreateFolder
+ viewController?.includeDirectoryE2EEncryption = true
+ self.present(navigationController, animated: true, completion: nil)
+ }
+
+ }
+
+ func changeCompressionImage(_ image: UIImage) -> UIImage {
+
+ var compressionQuality: CGFloat = 0.5
+ var baseHeight: Float = 595.2 // A4
+ var baseWidth: Float = 841.8 // A4
+
+ switch quality {
+ case .low:
+ baseHeight *= 1
+ baseWidth *= 1
+ compressionQuality = 0.3
+ case .medium:
+ baseHeight *= 2
+ baseWidth *= 2
+ compressionQuality = 0.6
+ case .high:
+ baseHeight *= 4
+ baseWidth *= 4
+ compressionQuality = 0.9
+ }
+
+ var newHeight = Float(image.size.height)
+ var newWidth = Float(image.size.width)
+ var imgRatio: Float = newWidth / newHeight
+ let baseRatio: Float = baseWidth / baseHeight
+
+ if newHeight > baseHeight || newWidth > baseWidth {
+ if imgRatio < baseRatio {
+ imgRatio = baseHeight / newHeight
+ newWidth = imgRatio * newWidth
+ newHeight = baseHeight
+ } else if imgRatio > baseRatio {
+ imgRatio = baseWidth / newWidth
+ newHeight = imgRatio * newHeight
+ newWidth = baseWidth
+ } else {
+ newHeight = baseHeight
+ newWidth = baseWidth
+ }
+ }
+
+ let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(newWidth), height: CGFloat(newHeight))
+ UIGraphicsBeginImageContext(rect.size)
+ image.draw(in: rect)
+ let img = UIGraphicsGetImageFromCurrentImageContext()
+ let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality))
+ UIGraphicsEndImageContext()
+ return UIImage(data: imageData!) ?? image
+ }
+
+ func bestFittingFont(for text: String, in bounds: CGRect, fontDescriptor: UIFontDescriptor, fontColor: UIColor) -> [NSAttributedString.Key: Any] {
+
+ let constrainingDimension = min(bounds.width, bounds.height)
+ let properBounds = CGRect(origin: .zero, size: bounds.size)
+ var attributes: [NSAttributedString.Key: Any] = [:]
+
+ let infiniteBounds = CGSize(width: CGFloat.infinity, height: CGFloat.infinity)
+ var bestFontSize: CGFloat = constrainingDimension
+
+ // Search font (H)
+ for fontSize in stride(from: bestFontSize, through: 0, by: -1) {
+ let newFont = UIFont(descriptor: fontDescriptor, size: fontSize)
+ attributes[.font] = newFont
+
+ let currentFrame = text.boundingRect(with: infiniteBounds, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attributes, context: nil)
+
+ if properBounds.contains(currentFrame) {
+ bestFontSize = fontSize
+ break
+ }
+ }
+
+ // Search kern (W)
+ let font = UIFont(descriptor: fontDescriptor, size: bestFontSize)
+ attributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: fontColor, NSAttributedString.Key.kern: 0] as [NSAttributedString.Key: Any]
+ for kern in stride(from: 0, through: 100, by: 0.1) {
+ let attributesTmp = [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: fontColor, NSAttributedString.Key.kern: kern] as [NSAttributedString.Key: Any]
+ let size = text.size(withAttributes: attributesTmp).width
+ if size <= bounds.width {
+ attributes = attributesTmp
+ } else {
+ break
+ }
+ }
+
+ return attributes
+ }
+
+ func isAtleastOneFiletypeSelected() -> Bool{
+ if(isPDFWithOCRSwitchOn
+ || isPDFWithoutOCRSwitchOn
+ || isTextFileSwitchOn
+ || isPNGFormatSwitchOn
+ || isJPGFormatSwitchOn){
+
+ return true
+ }else{
+ return false
+ }
+ }
+
+ func getPasswordFromField() -> String? {
+ if let setPasswordInputField : XLFormRowDescriptor = self.form.formRow(withTag: "SetPasswordInputField") {
+ var password = ""
+ if let indexPath = self.form.indexPath(ofFormRow: setPasswordInputField) {
+ let cell = tableView.cellForRow(at: indexPath) as? PasswordInputField
+ password = cell?.fileNameInputTextField.text ?? ""
+ }
+ return password
+ }
+ return nil
+ }
+}
+
diff --git a/iOSClient/Scan document/NMC Views/FileNameInputTextField.swift b/iOSClient/Scan document/NMC Views/FileNameInputTextField.swift
new file mode 100644
index 0000000000..e18ed84451
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/FileNameInputTextField.swift
@@ -0,0 +1,67 @@
+//
+// FileNameInputTextField.swift
+// Nextcloud
+//
+// Created by Sumit on 03/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class FileNameInputTextField: XLFormBaseCell,UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameInputTextField: UITextField!
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+ fileNameInputTextField.delegate = self
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameInputTextField == textField {
+ if let rowDescriptor = rowDescriptor, let text = self.fileNameInputTextField.text {
+
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameInputTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameInputTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameInputTextField)
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/FileNameInputTextField.xib b/iOSClient/Scan document/NMC Views/FileNameInputTextField.xib
new file mode 100644
index 0000000000..173862a3ae
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/FileNameInputTextField.xib
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.swift b/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.swift
new file mode 100644
index 0000000000..ba1ddd8537
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.swift
@@ -0,0 +1,44 @@
+//
+// JPGmageSaveSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 04/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+import XLForm
+
+class JPGImageSaveSwitchView: XLFormBaseCell{
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var switchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ // Do something
+
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.xib b/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.xib
new file mode 100644
index 0000000000..8658100453
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/JPGImageSaveSwitchView.xib
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.swift b/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.swift
new file mode 100644
index 0000000000..3c90b4d943
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.swift
@@ -0,0 +1,44 @@
+//
+// PNGImageSaveSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 09/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+import XLForm
+
+class PNGImageSaveSwitchView: XLFormBaseCell{
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var switchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ // Do something
+
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.xib b/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.xib
new file mode 100644
index 0000000000..d3c7312316
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PNGImageSaveSwitchView.xib
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/PasswordInputField.swift b/iOSClient/Scan document/NMC Views/PasswordInputField.swift
new file mode 100644
index 0000000000..43117f09f3
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PasswordInputField.swift
@@ -0,0 +1,73 @@
+//
+// PasswordInputField.swift
+// Nextcloud
+//
+// Created by Sumit on 10/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class PasswordInputField: XLFormBaseCell,UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameInputTextField: UITextField!
+ @IBOutlet weak var separatorBottom: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ fileNameInputTextField.isSecureTextEntry = true
+ fileNameInputTextField.delegate = self
+ separatorBottom.backgroundColor = NCBrandColor.shared.systemGray4
+ self.selectionStyle = .none
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ // fileNameInputTextField.isEnabled = false
+
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameInputTextField == textField {
+ if let rowDescriptor = rowDescriptor, let text = self.fileNameInputTextField.text {
+
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameInputTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameInputTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameInputTextField)
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/PasswordInputField.xib b/iOSClient/Scan document/NMC Views/PasswordInputField.xib
new file mode 100644
index 0000000000..26914e0760
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PasswordInputField.xib
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.swift b/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.swift
new file mode 100644
index 0000000000..5b0e76ddcc
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.swift
@@ -0,0 +1,43 @@
+//
+// PdfWithOcrSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 04/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class PdfWithOcrSwitchView: XLFormBaseCell{
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var switchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ // Do something
+
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.xib b/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.xib
new file mode 100644
index 0000000000..06cdcae9b6
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PdfWithOcrSwitchView.xib
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.swift b/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.swift
new file mode 100644
index 0000000000..7f0130f7c4
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.swift
@@ -0,0 +1,44 @@
+//
+// PdfWithoutOcrSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 04/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+import XLForm
+
+class PdfWithoutOcrSwitchView: XLFormBaseCell{
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var switchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ // Do something
+
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.xib b/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.xib
new file mode 100644
index 0000000000..46ebf565f7
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/PdfWithoutOcrSwitchView.xib
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/ScanDocumentPathView.swift b/iOSClient/Scan document/NMC Views/ScanDocumentPathView.swift
new file mode 100644
index 0000000000..666e4805fd
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/ScanDocumentPathView.swift
@@ -0,0 +1,34 @@
+//
+// ScanDocumentPathView.swift
+// Nextcloud
+//
+// Created by Sumit on 03/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class ScanDocumentPathView: XLFormButtonCell{
+
+ @IBOutlet weak var photoLabel: UILabel!
+ @IBOutlet weak var folderImage: UIImageView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+
+ }
+
+ override func configure() {
+ super.configure()
+
+
+ }
+
+ override func update() {
+ super.update()
+ }
+
+}
diff --git a/iOSClient/Scan document/NMC Views/ScanDocumentPathView.xib b/iOSClient/Scan document/NMC Views/ScanDocumentPathView.xib
new file mode 100644
index 0000000000..fa30321541
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/ScanDocumentPathView.xib
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.swift b/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.swift
new file mode 100644
index 0000000000..6090b4e787
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.swift
@@ -0,0 +1,53 @@
+//
+// SetPDFPasswordSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 10/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class SetPDFPasswordSwitchView: XLFormBaseCell{
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var switchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+ self.switchControl.isEnabled = false
+ }
+
+ override func update() {
+ super.update()
+
+ if let rowValue = rowDescriptor.value as? String {
+ if (rowValue == "disable_switch"){
+ self.switchControl.isEnabled = false
+ self.switchControl.isOn = false
+ }else if(rowValue == "enable_switch"){
+ self.switchControl.isEnabled = true
+ }
+ }
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ // Do something
+
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+}
diff --git a/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.xib b/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.xib
new file mode 100644
index 0000000000..e0ddc1ca1a
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/SetPDFPasswordSwitchView.xib
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.swift b/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.swift
new file mode 100644
index 0000000000..9ecac2bf75
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.swift
@@ -0,0 +1,41 @@
+//
+// TextFileWithOcrSwitchView.swift
+// Nextcloud
+//
+// Created by Sumit on 04/06/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import XLForm
+
+class TextFileWithOcrSwitchView: XLFormBaseCell{
+ @IBOutlet weak var switchControl: UISwitch!
+ @IBOutlet weak var cellLabel: UILabel!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ self.selectionStyle = .none
+ switchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+ }
+
+ override func configure() {
+ super.configure()
+
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ let value = mySwitch.isOn
+ if value {
+ //on
+ self.rowDescriptor.value = value
+ }else{
+ self.rowDescriptor.value = value
+ }
+ }
+
+}
diff --git a/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.xib b/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.xib
new file mode 100644
index 0000000000..5f0ff73601
--- /dev/null
+++ b/iOSClient/Scan document/NMC Views/TextFileWithOcrSwitchView.xib
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.swift b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
new file mode 100644
index 0000000000..58f2d55d8d
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
@@ -0,0 +1,35 @@
+//
+// AnalysisDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+class AnalysisDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var analysisDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ analysisDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ self.rowDescriptor.value = mySwitch.isOn
+ }
+}
+
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.xib b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
new file mode 100644
index 0000000000..45530193f6
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/CCAdvanced.h b/iOSClient/Settings/CCAdvanced.h
new file mode 100755
index 0000000000..7f9845888f
--- /dev/null
+++ b/iOSClient/Settings/CCAdvanced.h
@@ -0,0 +1,29 @@
+//
+// CCAdvanced.h
+// Nextcloud
+//
+// Created by Marino Faggiana on 12/04/17.
+// Copyright (c) 2017 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import
+#import
+
+@interface CCAdvanced : XLFormViewController
+
+@end
diff --git a/iOSClient/Settings/CCAdvanced.m b/iOSClient/Settings/CCAdvanced.m
new file mode 100755
index 0000000000..3c7477e5a3
--- /dev/null
+++ b/iOSClient/Settings/CCAdvanced.m
@@ -0,0 +1,635 @@
+//
+// CCManageHelp.m
+// Nextcloud
+//
+// Created by Marino Faggiana on 06/11/15.
+// Copyright (c) 2015 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import "CCAdvanced.h"
+#import "CCUtility.h"
+#import "NCBridgeSwift.h"
+#import "AdjustHelper.h"
+//#import
+
+@interface CCAdvanced ()
+{
+ AppDelegate *appDelegate;
+ XLFormSectionDescriptor *sectionSize;
+ TealiumHelper *tealium;
+ AdjustHelper *adjust;
+}
+@end
+
+@implementation CCAdvanced
+
+- (void)initializeForm
+{
+ XLFormDescriptor *form = [XLFormDescriptor formDescriptor];
+ XLFormSectionDescriptor *section;
+ XLFormRowDescriptor *row;
+
+ // Section HIDDEN FILES -------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"showHiddenFiles" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_show_hidden_files_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if ([[[NCKeychain alloc] init] showHiddenFiles]) row.value = @"1";
+ else row.value = @"0";
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Format Compatibility + Live Photo + Delete asset
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+ section.footerTitle = [NSString stringWithFormat:@"%@\n%@\n%@", NSLocalizedString(@"_format_compatibility_footer_", nil), NSLocalizedString(@"_upload_mov_livephoto_footer_", nil), NSLocalizedString(@"_remove_photo_CameraRoll_desc_", nil)];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"formatCompatibility" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_format_compatibility_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if ([[[NCKeychain alloc] init] formatCompatibility]) row.value = @"1";
+ else row.value = @"0";
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"livePhoto" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_upload_mov_livephoto_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if ([[[NCKeychain alloc] init] livePhoto]) row.value = @"1";
+ else row.value = @"0";
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"removePhotoCameraRoll" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_remove_photo_CameraRoll_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if ([[[NCKeychain alloc] init] removePhotoCameraRoll]) row.value = @"1";
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [section addFormRow:row];
+
+ // Section : Files App --------------------------------------------------------------
+
+ if (![NCBrandOptions shared].disable_openin_file) {
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+ section.footerTitle = NSLocalizedString(@"_disable_files_app_footer_", nil);
+
+ // Disable Files App
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"disablefilesapp" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_disable_files_app_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if ([[NCKeychain alloc] init].disableFilesApp) row.value = @"1";
+ else row.value = @"0";
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+ }
+
+ // Section : Privacy --------------------------------------------------------------
+
+ if (!NCBrandOptions.shared.disable_crash_service) {
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_privacy_", nil)];
+ [form addFormSection:section];
+ section.footerTitle = NSLocalizedString(@"_privacy_footer_", nil);
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"crashservice" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_crashservice_title_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"crashservice"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ if ([[[NCKeychain alloc] init] disableCrashservice]) row.value = @"1";
+ else row.value = @"0";
+ [section addFormRow:row];
+ }
+
+#ifdef DEBUG
+//#ifdef DEBUG
+ // Section DIAGNOSTICS -------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_diagnostics_", nil)];
+ [form addFormSection:section];
+
+// if ([[NSFileManager defaultManager] fileExistsAtPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] && NCBrandOptions.shared.disable_log == false) {
+ if ([[NSFileManager defaultManager] fileExistsAtPath:@""] && NCBrandOptions.shared.disable_log == false) {
+ if ([[NSFileManager defaultManager] fileExistsAtPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] && NCBrandOptions.shared.disable_log == false) {
+
+ if ([[NSFileManager defaultManager] fileExistsAtPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] && NCBrandOptions.shared.disable_log == false) {
+ // with Nextcloudkit latest version will uncomment below line once updated to latest Nextcloudkit version
+// if ([[NSFileManager defaultManager] fileExistsAtPath:NKLogFileManager.shared.logDirectory.path] && NCBrandOptions.shared.disable_log == false) {
+// if (NCBrandOptions.shared.disable_log) {
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"log" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_view_log_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"log"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.action.formBlock = ^(XLFormRowDescriptor * sender) {
+
+ [self deselectFormRow:sender];
+// NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] fileNameSource:@"" isEditingEnabled:false metadata:nil];
+ NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:@""] fileNameSource:@"" isEditingEnabled:false metadata:nil];
+ NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] isEditingEnabled:false metadata:nil];
+
+// NSURL *logFilePath = [self getLogFilePath];
+// if (logFilePath) {
+// // Use the log file path (e.g., to write logs)
+// NSLog(@"Log file path: %@", logFilePath.path);
+// NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:logFilePath fileNameSource:@"" isEditingEnabled:false metadata:nil];
+// [self presentViewController:viewerQuickLook animated:YES completion:nil];
+// } else {
+// // Handle error (logs folder could not be created)
+// NSLog(@"Failed to get log file path.");
+// }
+
+ NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NextcloudKit.shared.nkCommonInstance.filenamePathLog] fileNameSource:@"" isEditingEnabled:false metadata:nil];
+ // with Nextcloudkit latest version will uncomment below line once updated to latest Nextcloudkit version
+// NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NKLogFileManager.shared.logDirectory.path] fileNameSource:NKLogFileManager.shared.logFileName isEditingEnabled:false metadata:nil];
+// NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:@""] fileNameSource:@"" isEditingEnabled:false metadata:nil];
+ [self presentViewController:viewerQuickLook animated:YES completion:nil];
+ };
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"logLevel" rowType:XLFormRowDescriptorTypeSelectorPush title:NSLocalizedString(@"_set_log_level_", nil)];
+ NSInteger logLevel = [[NCKeychain alloc] init].logLevel;
+ row.value = @(logLevel);
+ switch ([[NCKeychain alloc] init].logLevel) {
+ case 0:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(0) displayText:NSLocalizedString(@"_disabled_", nil)];
+ break;
+ case 1:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(1) displayText:NSLocalizedString(@"_standard_", nil)];
+ break;
+ case 2:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(2) displayText:NSLocalizedString(@"_maximum_", nil)];
+ break;
+ default:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(1) displayText:NSLocalizedString(@"_standard_", nil)];
+ break;
+ }
+
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.cellConfigForSelector[@"tintColor"] = NCBrandColor.shared.customer;
+ row.selectorTitle = NSLocalizedString(@"_set_log_level_", nil);
+ row.selectorOptions = @[[XLFormOptionsObject formOptionsObjectWithValue:@(0) displayText:NSLocalizedString(@"_disabled_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(1) displayText:NSLocalizedString(@"_standard_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(2) displayText:NSLocalizedString(@"_maximum_", nil)],
+ ];
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"clearlog" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_clear_log_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"clear"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.action.formBlock = ^(XLFormRowDescriptor * sender) {
+
+ [self deselectFormRow:sender];
+
+// [[[NextcloudKit shared] nkCommonInstance] clearFileLog];
+ [[[NextcloudKit shared] nkCommonInstance] clearFileLog];
+ [[[NextcloudKit shared] nkCommonInstance] clearFileLog];
+ // with Nextcloudkit latest version will uncomment below line once updated to latest Nextcloudkit version
+// [[NKLogFileManager shared] clearLogFiles];
+// [self createLogFiles];
+
+ NSInteger logLevel = [[NCKeychain alloc] init].logLevel;
+ BOOL isSimulatorOrTestFlight = [[[NCUtility alloc] init] isSimulatorOrTestFlight];
+ NSString *versionNextcloudiOS = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudiOS, [[[NCUtility alloc] init] getVersionAppWithBuild:true]];
+// if (isSimulatorOrTestFlight) {
+// [[[NextcloudKit shared] nkCommonInstance] writeLog:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@ (Simulator / TestFlight)", (unsigned long)logLevel, versionNextcloudiOS]];
+// } else {
+// [[[NextcloudKit shared] nkCommonInstance] writeLog:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@", (unsigned long)logLevel, versionNextcloudiOS]];
+// }
+ if (isSimulatorOrTestFlight) {
+ [[[NextcloudKit shared] nkCommonInstance] writeLog:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@ (Simulator / TestFlight)", (unsigned long)logLevel, versionNextcloudiOS]];
+ } else {
+ [[[NextcloudKit shared] nkCommonInstance] writeLog:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@", (unsigned long)logLevel, versionNextcloudiOS]];
+ }
+ };
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"logLevel" rowType:XLFormRowDescriptorTypeSlider title:NSLocalizedString(@"_level_log_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:@(NSTextAlignmentCenter) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ NSInteger logLevel = [[NCKeychain alloc] init].logLevel;
+ row.value = @(logLevel);
+ [row.cellConfigAtConfigure setObject:@(2) forKey:@"slider.maximumValue"];
+ [row.cellConfigAtConfigure setObject:@(0) forKey:@"slider.minimumValue"];
+ [row.cellConfigAtConfigure setObject:@(2) forKey:@"steps"];
+ [section addFormRow:row];
+ }
+
+ // with Nextcloudkit latest version will uncomment below line once updated to latest Nextcloudkit version
+// if (isSimulatorOrTestFlight) {
+// [[NKLogFileManager shared] writeLogWithInfo:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@ (Simulator / TestFlight)", (unsigned long)logLevel, versionNextcloudiOS]];
+// } else {
+// [[NKLogFileManager shared] writeLogWithInfo:[NSString stringWithFormat:@"[INFO] Clear log with level %lu %@", (unsigned long)logLevel, versionNextcloudiOS]];
+// }
+ };
+ [section addFormRow:row];
+
+ }
+
+#ifdef DEBUG
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"capabilities" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_capabilities_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"capabilities"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.action.formBlock = ^(XLFormRowDescriptor * sender) {
+
+ [self deselectFormRow:sender];
+
+ UIViewController *vc = [[NCHostingCapabilitiesView alloc] makeShipDetailsUI];
+ [self.navigationController pushViewController:vc animated:YES];
+ };
+ [section addFormRow:row];
+#endif
+
+ // Section : Delete files / Clear cache --------------------------------------------------------------
+
+ sectionSize = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_delete_files_desc_", nil)];
+ [form addFormSection:sectionSize];
+ sectionSize.footerTitle = NSLocalizedString(@"_clear_cache_footer_", nil);
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"deleteoldfiles" rowType:XLFormRowDescriptorTypeSelectorPush title:NSLocalizedString(@"_delete_old_files_", nil)];
+
+ switch ([[NCKeychain alloc] init].cleanUpDay) {
+ case 0:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(0) displayText:NSLocalizedString(@"_never_", nil)];
+ break;
+ case 365:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(365) displayText:NSLocalizedString(@"_1_year_", nil)];
+ break;
+ case 180:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(180) displayText:NSLocalizedString(@"_6_months_", nil)];
+ break;
+ case 90:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(90) displayText:NSLocalizedString(@"_3_months_", nil)];
+ break;
+ case 30:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(30) displayText:NSLocalizedString(@"_1_month_", nil)];
+ break;
+ case 7:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(7) displayText:NSLocalizedString(@"_1_week_", nil)];
+ break;
+ case 1:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(1) displayText:NSLocalizedString(@"_1_day_", nil)];
+ break;
+ default:
+ row.value = [XLFormOptionsObject formOptionsObjectWithValue:@(0) displayText:NSLocalizedString(@"_never_", nil)];
+ break;
+ }
+
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.cellConfigForSelector[@"tintColor"] = NCBrandColor.shared.customer;
+ row.selectorTitle = NSLocalizedString(@"_delete_old_files_", nil);
+ row.selectorOptions = @[[XLFormOptionsObject formOptionsObjectWithValue:@(0) displayText:NSLocalizedString(@"_never_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(365) displayText:NSLocalizedString(@"_1_year_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(180) displayText:NSLocalizedString(@"_6_months_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(90) displayText:NSLocalizedString(@"_3_months_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(30) displayText:NSLocalizedString(@"_1_month_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(7) displayText:NSLocalizedString(@"_1_week_", nil)],
+ ];
+ [sectionSize addFormRow:row];
+
+ // Clear cache
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"azzeracache" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_clear_cache_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"trashIcon"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.action.formSelector = @selector(clearCacheRequest:);
+ [sectionSize addFormRow:row];
+
+ // Section EXIT --------------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+ section.footerTitle = NSLocalizedString(@"_exit_footer_", nil);
+
+ // Exit
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"esci" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_exit_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"xmark"] imageWithColor:[UIColor redColor] size:25] forKey:@"imageView.image"];
+ row.action.formSelector = @selector(exitNextcloud:);
+ [section addFormRow:row];
+
+ self.tableView.showsVerticalScrollIndicator = NO;
+ self.form = form;
+}
+
+// MARK: - View Life Cycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.title = NSLocalizedString(@"_advanced_", nil);
+ appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ adjust = [[AdjustHelper alloc] init];
+ self.tableView.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ [self initializeForm];
+ [self calculateSize];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ appDelegate.activeViewController = self;
+}
+
+#pragma mark -
+
+- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
+{
+ [super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
+
+ if ([rowDescriptor.tag isEqualToString:@"showHiddenFiles"]) {
+
+ [[NCKeychain alloc] init].showHiddenFiles = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"formatCompatibility"]) {
+
+ [[NCKeychain alloc] init].formatCompatibility = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"livePhoto"]) {
+
+ [[NCKeychain alloc] init].livePhoto = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"removePhotoCameraRoll"]) {
+
+ [[NCKeychain alloc] init].removePhotoCameraRoll = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"disablefilesapp"]) {
+
+ [[NCKeychain alloc] init].disableFilesApp = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"crashservice"]) {
+
+ [[NCKeychain alloc] init].disableCrashservice = [[rowDescriptor.value valueData] boolValue];
+
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"_crashservice_title_", nil) message:NSLocalizedString(@"_crashservice_alert_", nil) preferredStyle:UIAlertControllerStyleAlert];
+ UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"_ok_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ exit(0);
+ }];
+
+ [alertController addAction:okAction];
+ [self presentViewController:alertController animated:YES completion:nil];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"logLevel"]) {
+
+ NSInteger levelLog = [[rowDescriptor.value valueData] intValue];
+ [[NCKeychain alloc] init].logLevel = levelLog;
+// [[[NextcloudKit shared] nkCommonInstance] setLevelLog:levelLog];
+ [[[NextcloudKit shared] nkCommonInstance] setLevelLog:levelLog];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"deleteoldfiles"]) {
+
+ NSInteger days = [[rowDescriptor.value valueData] intValue];
+ [[NCKeychain alloc] init].cleanUpDay = days;
+ }
+}
+
+#pragma mark - Log files
+
+- (NSURL *)getLogFilePath {
+ // Define the log file name
+ NSString *logFileName = @"log.txt";
+
+ // Get the documents directory URL
+ NSArray *documentURLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
+ NSURL *documentsDirectory = [documentURLs firstObject];
+
+ // Define the logs folder URL
+ NSURL *logsFolder = [documentsDirectory URLByAppendingPathComponent:@"Logs" isDirectory:YES];
+
+ // Check if the "Logs" folder exists
+ if (![[NSFileManager defaultManager] fileExistsAtPath:logsFolder.path]) {
+ NSError *error = nil;
+ // Create the "Logs" folder if it doesn't exist
+ BOOL success = [[NSFileManager defaultManager] createDirectoryAtURL:logsFolder withIntermediateDirectories:YES attributes:nil error:&error];
+ if (!success) {
+ NSLog(@"Failed to create Logs folder: %@", error.localizedDescription);
+ return nil; // Return nil in case of error
+ }
+ }
+
+ // Create the full log file path
+ NSURL *logPath = [logsFolder URLByAppendingPathComponent:logFileName];
+
+ // Return the log file path
+ return logPath;
+}
+
+// Method to create log files
+- (void)createLogFiles {
+
+ // Property to control whether to copy log to the Documents directory
+ BOOL copyLogToDocumentDirectory;
+
+ copyLogToDocumentDirectory = (!NCBrandOptions.shared.disable_log) ? YES : NO;
+
+ // Define log file paths
+ NSString *filenamePathLog = @"/log.txt"; // Set your desired path for the log file
+ NSString *filenameLog = @"log.txt"; // This will be the filename to copy
+
+ // Create the log file at the given path
+ BOOL success = [[NSFileManager defaultManager] createFileAtPath:filenamePathLog contents:nil attributes:nil];
+ if (!success) {
+ NSLog(@"Failed to create file at path: %@", filenamePathLog);
+ }
+
+ // If we want to copy the log file to the Documents directory
+ if (copyLogToDocumentDirectory) { // Check the property value
+ NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentDirectory = [documentPaths firstObject];
+
+ // Create the path to copy the log file in Documents directory
+ NSString *filenameCopyToDocumentDirectory = [documentDirectory stringByAppendingPathComponent:filenameLog];
+
+ // Create the log file at the new path in the Documents directory
+ success = [[NSFileManager defaultManager] createFileAtPath:filenameCopyToDocumentDirectory contents:nil attributes:nil];
+ if (!success) {
+ NSLog(@"Failed to create file at Documents path: %@", filenameCopyToDocumentDirectory);
+ }
+ }
+}
+
+
+#pragma mark - Clear Cache
+
+- (void)clearCache:(NSString *)account
+{
+ [[NCNetworking shared] cancelAllTask];
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+
+ NCUtilityFileSystem *ufs = [[NCUtilityFileSystem alloc] init];
+
+ [[NSURLCache sharedURLCache] setMemoryCapacity:0];
+ [[NSURLCache sharedURLCache] setDiskCapacity:0];
+
+ [[NCManageDatabase shared] clearDatabaseWithAccount:account removeAccount:false];
+ [[NCManageDatabase shared] clearDatabaseWithAccount:account removeAccount:false removeAutoUpload:false];
+// [[NCManageDatabase shared] clearDatabaseWithAccount:account removeAccount:false];
+
+ [ufs removeGroupDirectoryProviderStorage];
+ [ufs removeGroupLibraryDirectory];
+
+ [ufs removeDocumentsDirectory];
+ [ufs removeTemporaryDirectory];
+
+ [ufs createDirectoryStandard];
+
+ [[NCAutoUpload shared] alignPhotoLibraryWithController:self account:appDelegate.account];
+ [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+// [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+
+ [[NCImageCache shared] createMediaCacheWithAccount:appDelegate.account withCacheSize:true];
+
+ [[NCActivityIndicator shared] stop];
+ tealium = [[TealiumHelper alloc] init];
+ [tealium trackEventWithTitle:@"magentacloud-app.settings.reset" data:nil];
+ [adjust trackEvent:ResetsApp];
+ [self calculateSize];
+ });
+}
+
+- (void)clearCacheRequest:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"_want_delete_cache_", nil) preferredStyle:UIAlertControllerStyleActionSheet];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_yes_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ [[NCActivityIndicator shared] startActivityWithBackgroundView:nil style: UIActivityIndicatorViewStyleLarge blurEffect:true];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+ [self clearCache:appDelegate.account];
+ });
+ }]];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_cancel_", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
+ }]];
+
+ alertController.popoverPresentationController.sourceView = self.view;
+ NSIndexPath *indexPath = [self.form indexPathOfFormRow:sender];
+ CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath];
+ alertController.popoverPresentationController.sourceRect = CGRectOffset(cellRect, -self.tableView.contentOffset.x, -self.tableView.contentOffset.y);
+
+ [self presentViewController:alertController animated:YES completion:nil];
+}
+
+
+- (void)clearAllCacheRequest:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ [[NCActivityIndicator shared] startActivityWithBackgroundView:nil style: UIActivityIndicatorViewStyleLarge blurEffect:true];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+ [self clearCache:nil];
+ });
+}
+
+- (void)calculateSize
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NCUtilityFileSystem *ufs = [[NCUtilityFileSystem alloc] init];
+ NSString *directory = [ufs directoryProviderStorage];
+ int64_t totalSize = [ufs getDirectorySizeWithDirectory:directory];
+ sectionSize.footerTitle = [NSString stringWithFormat:@"%@. (%@ %@)", NSLocalizedString(@"_clear_cache_footer_", nil), NSLocalizedString(@"_used_space_", nil), [ufs transformedSize:totalSize]];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.tableView reloadData];
+ });
+ });
+}
+
+#pragma mark - Exit Nextcloud
+
+- (void)exitNextcloud:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"_want_exit_", nil) preferredStyle:UIAlertControllerStyleActionSheet];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_ok_", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
+ tealium = [[TealiumHelper alloc] init];
+ [tealium trackEventWithTitle:@"magentacloud-app.settings.logout" data:nil];
+ [adjust trackEvent:Logout];
+ [appDelegate resetApplication];
+ }]];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_cancel_", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
+ }]];
+
+ alertController.popoverPresentationController.sourceView = self.view;
+ NSIndexPath *indexPath = [self.form indexPathOfFormRow:sender];
+ CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath];
+ alertController.popoverPresentationController.sourceRect = CGRectOffset(cellRect, -self.tableView.contentOffset.x, -self.tableView.contentOffset.y);
+
+ [self presentViewController:alertController animated:YES completion:nil];
+}
+
+#pragma mark -
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ if (indexPath.section == 4 && indexPath.row == 2) {
+ return 80;
+ } else {
+ return NCGlobal.shared.heightCellSettings;
+ }
+}
+
+@end
diff --git a/iOSClient/Settings/CCManageAccount.h b/iOSClient/Settings/CCManageAccount.h
new file mode 100644
index 0000000000..48a072c44f
--- /dev/null
+++ b/iOSClient/Settings/CCManageAccount.h
@@ -0,0 +1,28 @@
+//
+// CCManageAccount.h
+// Nextcloud
+//
+// Created by Marino Faggiana on 12/03/15.
+// Copyright (c) 2015 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import
+
+@interface CCManageAccount : XLFormViewController
+
+@end
diff --git a/iOSClient/Settings/CCManageAccount.m b/iOSClient/Settings/CCManageAccount.m
new file mode 100644
index 0000000000..8c06b8e244
--- /dev/null
+++ b/iOSClient/Settings/CCManageAccount.m
@@ -0,0 +1,443 @@
+//
+// CCManageAccount.m
+// Nextcloud
+//
+// Created by Marino Faggiana on 12/03/15.
+// Copyright (c) 2015 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import "CCManageAccount.h"
+#import "NCBridgeSwift.h"
+#import "CCUtility.h"
+
+#define actionSheetCancellaAccount 1
+
+@interface CCManageAccount ()
+{
+ AppDelegate *appDelegate;
+}
+@end
+
+@implementation CCManageAccount
+
+- (void)initializeForm
+{
+ XLFormDescriptor *form = [XLFormDescriptor formDescriptor];
+ XLFormSectionDescriptor *section;
+ XLFormRowDescriptor *row;
+
+ NSArray *accounts = [[NCManageDatabase shared] getAllTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+
+ // Section : ACCOUNTS -------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_accounts_", nil) sectionOptions:XLFormSectionOptionCanDelete];
+ [form addFormSection:section];
+
+ for (tableAccount *account in accounts) {
+
+ NSString *title = [NSString stringWithFormat:@"%@ %@", account.user, [NSURL URLWithString:account.urlBase].host];
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:account.account rowType:XLFormRowDescriptorTypeBooleanCheck title:title];
+
+ // Avatar
+ UIImage *avatar = [[[NCUtility alloc] init] loadUserImageFor:account.user displayName:account.displayName urlBase:account.urlBase];
+
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:13.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:avatar forKey:@"imageView.image"];
+ if (account.active) {
+ row.value = @"YES";
+ }
+ [section addFormRow:row];
+ }
+
+ // Section : ALIAS --------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_alias_", nil)];
+ section.footerTitle = NSLocalizedString(@"_alias_footer_", nil);
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"alias" rowType:XLFormRowDescriptorTypeText];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[[UIImage imageNamed:@"form-textbox"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textField.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textField.textColor"];
+ row.value = activeAccount.alias;
+ [section addFormRow:row];
+
+ // Section : MANAGE ACCOUNT -------------------------------------------
+
+ if ([NCBrandOptions shared].disable_multiaccount == NO) {
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_manage_account_", nil)];
+ [form addFormSection:section];
+
+ if ([NCBrandOptions shared].disable_multiaccount == NO) {
+
+ // New Account nextcloud
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"addAccount" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_add_account_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage systemImageNamed:@"plus"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.formSelector = @selector(addAccount:);
+ [section addFormRow:row];
+ }
+
+ // Set user status
+
+ BOOL userStatus = [[NCGlobal shared] capabilityUserStatusEnabled];
+ if (userStatus) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"setUserStatus" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_set_user_status_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"userStatusAway"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ row.action.formSelector = @selector(setUserStatus:);
+ if (accounts.count == 0) row.disabled = @YES;
+ [section addFormRow:row];
+ }
+
+ if ([NCBrandOptions shared].disable_multiaccount == NO) {
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"accountRequest" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_settings_account_request_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[[UIImage imageNamed:@"users"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ if ([[NCKeychain alloc] init].accountRequest) row.value = @1;
+ else row.value = @0;
+ [section addFormRow:row];
+ }
+ }
+
+ // Section : CERIFICATES -------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_certificates_", nil)];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"certificateDetails" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_certificate_details_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.formSelector = @selector(certificateDetails:);
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"certificatePNDetails" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_certificate_pn_details_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.formSelector = @selector(certificatePNDetails:);
+ [section addFormRow:row];
+
+ // Section : USER INFORMATION -------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_personal_information_", nil)];
+ [form addFormSection:section];
+
+ // Full Name
+ if ([activeAccount.displayName length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"userfullname" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_full_name_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"user"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.displayName;
+ [section addFormRow:row];
+ }
+
+ // Address
+ if ([activeAccount.address length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"useraddress" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_address_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"address"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.address;
+ [section addFormRow:row];
+ }
+
+ // City + zip
+ if ([activeAccount.city length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"usercity" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_city_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"city"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.city;
+ if ([activeAccount.zip length] > 0) {
+ row.value = [NSString stringWithFormat:@"%@ %@", row.value, activeAccount.zip];
+ }
+ [section addFormRow:row];
+ }
+
+ // Country
+ if ([activeAccount.country length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"usercountry" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_country_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"country"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = [[NSLocale systemLocale] displayNameForKey:NSLocaleCountryCode value:activeAccount.country];
+ [section addFormRow:row];
+ }
+
+ // Phone
+ if ([activeAccount.phone length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"userphone" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_phone_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"phone"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.phone;
+ [section addFormRow:row];
+ }
+
+ // Email
+ if ([activeAccount.email length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"useremail" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_email_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"email"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.email;
+ [section addFormRow:row];
+ }
+
+ // Web
+ if ([activeAccount.website length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"userweb" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_web_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"network"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.website;
+ [section addFormRow:row];
+ }
+
+ // Twitter
+ if ([activeAccount.twitter length] > 0) {
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"usertwitter" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_twitter_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"twitter"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ row.value = activeAccount.twitter;
+ [section addFormRow:row];
+ }
+
+ self.tableView.showsVerticalScrollIndicator = NO;
+ self.form = form;
+
+ // Open Login
+ if (accounts.count == 0) {
+ [appDelegate openLoginWithViewController:self selector:NCGlobal.shared.introLogin openLoginWeb:false];
+ }
+}
+
+// MARK: - View Life Cycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.title = NSLocalizedString(@"_credentials_", nil);
+ appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+
+ self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ self.tableView.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeUser) name:NCGlobal.shared.notificationCenterChangeUser object:nil];
+
+ [self initializeForm];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ appDelegate.activeViewController = self;
+}
+
+#pragma mark - NotificationCenter
+
+- (void)changeUser
+{
+ [self initializeForm];
+}
+
+#pragma mark -
+
+-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
+{
+ [super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
+
+ if ([rowDescriptor.tag isEqualToString:@"accountRequest"]) {
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+ [[NCKeychain alloc] init].accountRequest = true;
+ } else {
+ [[NCKeychain alloc] init].accountRequest = false;
+ }
+ }
+
+ else if ([rowDescriptor.tag isEqualToString:@"alias"]) {
+
+ if ([newValue isEqual:[NSNull null]]) {
+ [[NCManageDatabase shared] setAccountAlias:@""];
+ } else {
+ [[NCManageDatabase shared] setAccountAlias:newValue];
+ }
+ }
+
+ else {
+
+ NSArray *accounts = [[NCManageDatabase shared] getAllTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+
+ for (tableAccount *account in accounts) {
+ if ([rowDescriptor.tag isEqualToString:account.account]) {
+ if (![account.account isEqualToString:activeAccount.account]) {
+ [appDelegate changeAccount:account.account userProfile:nil];
+ }
+ }
+ }
+
+ [self initializeForm];
+ }
+}
+
+-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return NSLocalizedString(@"_remove_local_account_", nil);
+}
+
+-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+ [super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
+
+ if (editingStyle == UITableViewCellEditingStyleDelete) {
+
+ [self initializeForm];
+
+ NSArray *accounts = [[NCManageDatabase shared] getAllTableAccount];
+ tableAccount *tableAccountForDelete = accounts[indexPath.row];
+ tableAccount *tableActiveAccount = [[NCManageDatabase shared] getActiveTableAccount];
+
+ NSString *accountForDelete = tableAccountForDelete.account;
+ NSString *activeAccount = tableActiveAccount.account;
+
+ NSString *title = [NSString stringWithFormat:NSLocalizedString(@"_want_delete_account_",nil), accountForDelete];
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_remove_local_account_", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
+
+ if (accountForDelete) {
+ [appDelegate deleteAccount:accountForDelete wipe:false];
+ }
+
+ NSArray *listAccount = [[NCManageDatabase shared] getAccounts];
+ if ([listAccount count] > 0) {
+ if ([accountForDelete isEqualToString:activeAccount]) {
+ [appDelegate changeAccount:listAccount[0] userProfile:nil];
+ }
+ }
+
+ [self initializeForm];
+ }]];
+
+ [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_cancel_", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { }]];
+
+ alertController.popoverPresentationController.sourceView = self.view;
+ alertController.popoverPresentationController.sourceRect = [self.tableView rectForRowAtIndexPath:indexPath];
+
+ [self presentViewController:alertController animated:YES completion:nil];
+ }
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.section == 0) {
+ return 60;
+ } else {
+ return NCGlobal.shared.heightCellSettings;
+ }
+}
+
+#pragma mark -
+
+- (void)addAccount:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+// [appDelegate openLoginWithViewController:self selector:NCGlobal.shared.introLogin openLoginWeb:false];
+}
+
+#pragma mark -
+
+- (void)setUserStatus:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@"NCUserStatus" bundle:nil] instantiateInitialViewController];
+ [self presentViewController:navigationController animated:YES completion:nil];
+}
+
+#pragma mark -
+
+- (void)certificateDetails:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@"NCViewCertificateDetails" bundle:nil] instantiateInitialViewController];
+ NCViewCertificateDetails *viewController = (NCViewCertificateDetails *)navigationController.topViewController;
+
+ NSURL *url = [NSURL URLWithString:appDelegate.urlBase];
+ viewController.host = [url host];
+
+ [self presentViewController:navigationController animated:YES completion:nil];
+}
+
+- (void)certificatePNDetails:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@"NCViewCertificateDetails" bundle:nil] instantiateInitialViewController];
+ NCViewCertificateDetails *viewController = (NCViewCertificateDetails *)navigationController.topViewController;
+
+ NSURL *url = [NSURL URLWithString: NCBrandOptions.shared.pushNotificationServerProxy];
+ viewController.host = [url host];
+ viewController.certificateTitle = NSLocalizedString(@"_certificate_pn_view_", nil);
+
+ [self presentViewController:navigationController animated:YES completion:nil];
+}
+
+@end
diff --git a/iOSClient/Settings/CCManageAutoUpload.h b/iOSClient/Settings/CCManageAutoUpload.h
new file mode 100644
index 0000000000..1e6b701cfc
--- /dev/null
+++ b/iOSClient/Settings/CCManageAutoUpload.h
@@ -0,0 +1,28 @@
+//
+// CCManageAutoUpload.h
+// Nextcloud
+//
+// Created by Marino Faggiana on 01/09/15.
+// Copyright (c) 2015 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import
+
+@interface CCManageAutoUpload : XLFormViewController
+
+@end
diff --git a/iOSClient/Settings/CCManageAutoUpload.m b/iOSClient/Settings/CCManageAutoUpload.m
new file mode 100644
index 0000000000..8f78acc6f1
--- /dev/null
+++ b/iOSClient/Settings/CCManageAutoUpload.m
@@ -0,0 +1,547 @@
+//
+// CCManageAutoUpload.m
+// Nextcloud
+//
+// Created by Marino Faggiana on 01/09/15.
+// Copyright (c) 2015 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import
+#import "CCManageAutoUpload.h"
+#import "CCUtility.h"
+#import "NCBridgeSwift.h"
+#import "AdjustHelper.h"
+
+@interface CCManageAutoUpload ()
+{
+ AppDelegate *appDelegate;
+ AdjustHelper *adjust;
+ TealiumHelper *tealium;
+}
+@end
+
+@implementation CCManageAutoUpload
+
+- (void)initializeForm
+{
+ XLFormDescriptor *form = [XLFormDescriptor formDescriptor];
+ XLFormSectionDescriptor *section;
+ XLFormRowDescriptor *row;
+
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveAccount];
+
+
+ // Auto Upload
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+ section.footerTitle = NSLocalizedString(@"_autoupload_description_", nil);
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUpload" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_autoupload_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ if (activeAccount.autoUpload) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Auto Upload Directory
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadDirectory" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_autoupload_select_folder_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"foldersOnTop"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"foldersOnTop"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ //[row.cellConfig setObject:@(UITableViewCellAccessoryDisclosureIndicator) forKey:@"accessoryType"];
+ row.action.formSelector = @selector(selectAutomaticUploadFolder);
+ [section addFormRow:row];
+
+ // Auto Upload Photo
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadImage" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_autoupload_photos_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if (activeAccount.autoUploadImage) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadWWAnPhoto" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_wifi_only_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if (activeAccount.autoUploadWWAnPhoto) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Auto Upload Video
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadVideo" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_autoupload_videos_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if (activeAccount.autoUploadVideo) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadWWAnVideo" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_wifi_only_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if (activeAccount.autoUploadWWAnVideo) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Delete asset remove photo camera roll
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"removePhotoCameraRoll" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_remove_photo_CameraRoll_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if ([[[NCKeychain alloc] init] removePhotoCameraRoll]) row.value = @"1";
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Auto Upload Full
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ NSString *title = NSLocalizedString(@"_autoupload_fullphotos_", nil);
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadFull" rowType:XLFormRowDescriptorTypeBooleanSwitch title:title];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ row.value = 0;
+ if (activeAccount.autoUploadFull) row.value = @1;
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadStart" rowType:XLFormRowDescriptorTypeBooleanSwitch title:title];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ row.value = 0;
+ if (activeAccount.autoUploadStart) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ // Auto Upload create subfolder
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadCreateSubfolder" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_autoupload_create_subfolder_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ if (activeAccount.autoUploadCreateSubfolder) row.value = @1;
+ else row.value = @0;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ [section addFormRow:row];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadSubfolderGranularity" rowType:XLFormRowDescriptorTypeSelectorPush title:NSLocalizedString(@"_autoupload_subfolder_granularity_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ row.cellConfigForSelector[@"tintColor"] = NCBrandColor.shared.customer;
+ row.selectorOptions = @[
+ [XLFormOptionsObject formOptionsObjectWithValue:@(NCGlobal.shared.subfolderGranularityYearly) displayText:NSLocalizedString(@"_yearly_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(NCGlobal.shared.subfolderGranularityMonthly) displayText:NSLocalizedString(@"_monthly_", nil)],
+ [XLFormOptionsObject formOptionsObjectWithValue:@(NCGlobal.shared.subfolderGranularityDaily) displayText:NSLocalizedString(@"_daily_", nil)]
+ ];
+ row.value = row.selectorOptions[activeAccount.autoUploadSubfolderGranularity];
+ row.required = true;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [section addFormRow:row];
+
+ // Auto Upload file name
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadFileName" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_autoupload_filenamemask_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.viewControllerClass = [NCManageAutoUploadFileName class];
+ [section addFormRow:row];
+
+ // end
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ self.tableView.showsVerticalScrollIndicator = NO;
+ self.form = form;
+}
+
+// MARK: - View Life Cycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.title = NSLocalizedString(@"_settings_autoupload_", nil);
+ appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ adjust = [[AdjustHelper alloc] init];
+ [adjust configAdjust];
+ tealium = [[TealiumHelper alloc] init];
+ self.tableView.backgroundColor = UIColor.systemGroupedBackgroundColor;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeUser) name:NCGlobal.shared.notificationCenterChangeUser object:nil];
+
+ [self initializeForm];
+ [self reloadForm];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ appDelegate.activeViewController = self;
+ [[[NCAskAuthorization alloc] init] askAuthorizationPhotoLibraryWithController:self completion:^(BOOL status) { }];
+ [[[NCAskAuthorization alloc] init] askAuthorizationPhotoLibraryWithViewController:self completion:^(BOOL status) { }];
+ appDelegate.account = [[[NCKeychain alloc] init] getAccountName] ;
+ [[[NCAskAuthorization alloc] init] askAuthorizationPhotoLibraryWithController:self completion:^(BOOL status) { }];
+}
+
+- (void)changeUser
+{
+ // [[self navigationController] popViewControllerAnimated:YES];
+ [self initializeForm];
+ [self reloadForm];
+}
+
+#pragma mark - NotificationCenter
+
+
+#pragma mark -
+
+-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
+{
+ [super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
+
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveAccount];
+
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUpload"]) {
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUpload" state:YES];
+
+ // Default
+ [[NCManageDatabase shared] setAccountAutoUploadFileName:nil];
+ [[NCManageDatabase shared] setAccountAutoUploadDirectory:nil urlBase:appDelegate.urlBase userId:appDelegate.userId account:appDelegate.account];
+
+ // verifichiamo che almeno uno dei servizi (foto video) siano attivi, in caso contrario attiviamo le foto
+ if (activeAccount.autoUploadImage == NO && activeAccount.autoUploadVideo == NO) {
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadImage" state:YES];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadVideo" state:YES];
+ }
+
+ [[NCAutoUpload shared] alignPhotoLibraryWithController:self account:appDelegate.account];
+
+ [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+// [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+
+ } else {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUpload" state:NO];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadFull" state:NO];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadStart" state:NO];
+
+ // remove
+ [[NCManageDatabase shared] clearMetadatasUploadWithAccount:appDelegate.account];
+ }
+
+ [self reloadForm];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"removePhotoCameraRoll"]) {
+
+ [[NCKeychain alloc] init].removePhotoCameraRoll = [[rowDescriptor.value valueData] boolValue];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadFull"]) {
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadStart"]) {
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+
+ [[NCAutoUpload shared] autoUploadFullPhotosWithController:self log:@"Auto upload full" account:appDelegate.account];
+ [[NCAutoUpload shared] autoUploadFullPhotosWithViewController:self log:@"Auto upload full"];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadFull" state:YES];
+// [[NCAutoUpload shared] autoUploadFullPhotosWithViewController:self log:@"Auto upload full"];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadStart" state:YES];
+
+ } else {
+
+ [[NCManageDatabase shared] clearMetadatasUploadWithAccount:appDelegate.account];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadFull" state:NO];
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadStart" state:NO];
+ }
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadImage"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadImage" state:[[rowDescriptor.value valueData] boolValue]];
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+ [[NCAutoUpload shared] alignPhotoLibraryWithController:self account:appDelegate.account];
+ [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+// [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+ }
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadWWAnPhoto"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadWWAnPhoto" state:[[rowDescriptor.value valueData] boolValue]];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadVideo"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadVideo" state:[[rowDescriptor.value valueData] boolValue]];
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES){
+ [[NCAutoUpload shared] alignPhotoLibraryWithController:self account:appDelegate.account];
+ [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+// [[NCAutoUpload shared] alignPhotoLibraryWithViewController:self];
+ }
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadWWAnVideo"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadWWAnVideo" state:[[rowDescriptor.value valueData] boolValue]];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadCreateSubfolder"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadCreateSubfolder" state:[[rowDescriptor.value valueData] boolValue]];
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"autoUploadSubfolderGranularity"]) {
+
+ [[NCManageDatabase shared] setAccountAutoUploadGranularity:@"autoUploadSubfolderGranularity" state:[[rowDescriptor.value valueData] integerValue]];
+ }
+}
+
+- (void)done:(XLFormRowDescriptor *)sender
+{
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)reloadForm
+{
+ self.form.delegate = nil;
+
+ XLFormRowDescriptor *rowAutoUpload = [self.form formRowWithTag:@"autoUpload"];
+
+ XLFormRowDescriptor *rowAutoUploadImage = [self.form formRowWithTag:@"autoUploadImage"];
+ XLFormRowDescriptor *rowAutoUploadWWAnPhoto = [self.form formRowWithTag:@"autoUploadWWAnPhoto"];
+
+ XLFormRowDescriptor *rowAutoUploadVideo = [self.form formRowWithTag:@"autoUploadVideo"];
+ XLFormRowDescriptor *rowAutoUploadWWAnVideo = [self.form formRowWithTag:@"autoUploadWWAnVideo"];
+ XLFormRowDescriptor *rowRemovePhotoCameraRoll = [self.form formRowWithTag:@"removePhotoCameraRoll"];
+
+ XLFormRowDescriptor *rowAutoUploadFull = [self.form formRowWithTag:@"autoUploadFull"];
+ XLFormRowDescriptor *rowAutoUploadFull = [self.form formRowWithTag:@"autoUploadStart"];
+
+ XLFormRowDescriptor *rowAutoUploadCreateSubfolder = [self.form formRowWithTag:@"autoUploadCreateSubfolder"];
+
+ XLFormRowDescriptor *rowAutoUploadSubfolderGranularity = [self.form formRowWithTag:@"autoUploadSubfolderGranularity"];
+
+ XLFormRowDescriptor *rowAutoUploadFileName = [self.form formRowWithTag:@"autoUploadFileName"];
+
+ // - STATUS ---------------------
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveAccount];
+
+ if (activeAccount.autoUpload)
+ [rowAutoUpload setValue:@1]; else [rowAutoUpload setValue:@0];
+
+ if (activeAccount.autoUploadImage)
+ [rowAutoUploadImage setValue:@1]; else [rowAutoUploadImage setValue:@0];
+
+ if (activeAccount.autoUploadWWAnPhoto)
+ [rowAutoUploadWWAnPhoto setValue:@1]; else [rowAutoUploadWWAnPhoto setValue:@0];
+
+ if ([[[NCKeychain alloc] init] removePhotoCameraRoll])
+ [rowRemovePhotoCameraRoll setValue:@1]; else [rowRemovePhotoCameraRoll setValue:@0];
+
+ if (activeAccount.autoUploadVideo)
+ [rowAutoUploadVideo setValue:@1]; else [rowAutoUploadVideo setValue:@0];
+
+ if (activeAccount.autoUploadWWAnVideo)
+ [rowAutoUploadWWAnVideo setValue:@1]; else [rowAutoUploadWWAnVideo setValue:@0];
+
+ if (activeAccount.autoUploadFull)
+ if (activeAccount.autoUploadStart)
+ [rowAutoUploadFull setValue:@1]; else [rowAutoUploadFull setValue:@0];
+
+ if (activeAccount.autoUploadCreateSubfolder)
+ [rowAutoUploadCreateSubfolder setValue:@1]; else [rowAutoUploadCreateSubfolder setValue:@0];
+
+ [rowAutoUploadSubfolderGranularity setValue:rowAutoUploadSubfolderGranularity.selectorOptions[activeAccount.autoUploadSubfolderGranularity]];
+
+ // - HIDDEN --------------------------------------------------------------------------
+
+ rowAutoUploadImage.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ rowAutoUploadWWAnPhoto.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ rowAutoUploadVideo.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ rowAutoUploadWWAnVideo.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+ rowRemovePhotoCameraRoll.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ rowAutoUploadFull.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ rowAutoUploadCreateSubfolder.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ rowAutoUploadSubfolderGranularity.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ rowAutoUploadFileName.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"];
+
+ // -----------------------------------------------------------------------------------
+
+ [self.tableView reloadData];
+
+ self.form.delegate = self;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return NCGlobal.shared.heightCellSettings;
+}
+
+- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
+{
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveTableAccount];
+ tableAccount *activeAccount = [[NCManageDatabase shared] getActiveAccount];
+ NSString *sectionName;
+ NSString *autoUploadPath = [NSString stringWithFormat:@"%@/%@", [[NCManageDatabase shared] getAccountAutoUploadDirectoryWithUrlBase:appDelegate.urlBase userId:appDelegate.userId account:appDelegate.account], [[NCManageDatabase shared] getAccountAutoUploadFileName]];
+
+ switch (section)
+ {
+ case 0:
+ sectionName = NSLocalizedString(@"_autoupload_description_", nil);
+ break;
+ case 1:
+ if (activeAccount.autoUpload) sectionName = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"_autoupload_current_folder_", nil), [self returnPathfromServerUrl:autoUploadPath urlBase:appDelegate.urlBase userId:appDelegate.userId account:appDelegate.account]];
+ else sectionName = @"";
+ break;
+ case 4:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_remove_photo_CameraRoll_desc_", nil);
+ else sectionName = @"";
+ break;
+ case 5:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_description_background_", nil);
+ else sectionName = @"";
+ break;
+ case 6:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_create_subfolder_footer_", nil);
+ else sectionName = @"";
+ break;
+ case 7:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_fullphotos_footer_", nil);
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_fullphotos_footer_", nil);
+ else sectionName = @"";
+ break;
+ case 5:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_create_subfolder_footer_", nil);
+ else sectionName = @"";
+ break;
+ case 6:
+ if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_filenamemask_footer_", nil);
+ else sectionName = @"";
+ break;
+ }
+ return sectionName;
+}
+
+- (void)dismissSelectWithServerUrl:(NSString * _Nullable)serverUrl metadata:(tableMetadata * _Nullable)metadata type:(NSString * _Nonnull)type items:(NSArray * _Nonnull)items overwrite:(BOOL)overwrite copy:(BOOL)copy move:(BOOL)move
+- (void)dismissSelectWithServerUrl:(NSString * _Nullable)serverUrl metadata:(tableMetadata * _Nullable)metadata type:(NSString * _Nonnull)type items:(NSArray * _Nonnull)items overwrite:(BOOL)overwrite copy:(BOOL)copy move:(BOOL)move
+{
+ if (serverUrl != nil) {
+
+ NSString* home = [[[NCUtilityFileSystem alloc] init] getHomeServerWithUrlBase:appDelegate.urlBase userId:appDelegate.userId];
+ if ([serverUrl isEqualToString:home]) {
+// NKError *error = [[NKError alloc] initWithErrorCode:NCGlobal.shared.errorInternalError errorDescription:@"_autoupload_error_select_folder_"];// responseData:nil];
+// [[[NCContentPresenter alloc] init] messageNotification:@"_error_" error:error delay:[[NCGlobal shared] dismissAfterSecond] type:messageTypeError afterDelay:0];
+ NKError *error = [[NKError alloc] initWithErrorCode:NCGlobal.shared.errorInternalError errorDescription:@"_autoupload_error_select_folder_" responseData:nil];
+ [[[NCContentPresenter alloc] init] messageNotification:@"_error_" error:error delay:[[NCGlobal shared] dismissAfterSecond] type:messageTypeError afterDelay:0];
+ return;
+ }
+
+ // Settings new folder Automatatic upload
+ [[NCManageDatabase shared] setAccountAutoUploadFileName:serverUrl.lastPathComponent];
+ NSString *path = [[[NCUtilityFileSystem alloc] init] deleteLastPathWithServerUrlPath:serverUrl home:home];
+ if (path != nil) {
+ [[NCManageDatabase shared] setAccountAutoUploadDirectory:path urlBase:appDelegate.urlBase userId:appDelegate.userId account:appDelegate.account];
+ }
+ // Reload
+ [self.tableView reloadData];
+ }
+}
+
+- (void)selectAutomaticUploadFolder
+ {
+ UINavigationController *navigationController = [[UIStoryboard storyboardWithName:@"NCSelect" bundle:nil] instantiateInitialViewController];
+ NCSelect *viewController = (NCSelect *)navigationController.topViewController;
+
+ viewController.delegate = self;
+ viewController.typeOfCommandView = 1;
+
+ [self presentViewController:navigationController animated:YES completion:^{
+ [self.tableView reloadData];
+ }];
+ }
+
+- (NSString *)returnPathfromServerUrl:(NSString *)serverUrl urlBase:(NSString *)urlBase userId:(NSString *)userId account:(NSString *)account
+{
+ NSString *homeServer = [[[NCUtilityFileSystem alloc] init] getHomeServerWithUrlBase:urlBase userId:userId];
+ NSString *path = [serverUrl stringByReplacingOccurrencesOfString:homeServer withString:@""];
+ return path;
+}
+
+@end
diff --git a/iOSClient/Settings/HelpViewController.swift b/iOSClient/Settings/HelpViewController.swift
new file mode 100644
index 0000000000..eb91d7b4cf
--- /dev/null
+++ b/iOSClient/Settings/HelpViewController.swift
@@ -0,0 +1,57 @@
+//
+// HelpViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 11/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import WebKit
+
+class HelpViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view, typically from a nib.
+
+ self.title = NSLocalizedString("_help_", comment: "")
+ let myWebView:WKWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://cloud.telekom-dienste.de/hilfe")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
+
diff --git a/iOSClient/Settings/ImprintViewController.swift b/iOSClient/Settings/ImprintViewController.swift
new file mode 100644
index 0000000000..819a86599d
--- /dev/null
+++ b/iOSClient/Settings/ImprintViewController.swift
@@ -0,0 +1,70 @@
+//
+// ImprintViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 11/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import WebKit
+
+class ImprintViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view, typically from a nib.
+ self.title = NSLocalizedString("_imprint_", comment: "")
+
+ let myWebView:WKWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://www.telekom.de/impressum")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+ super.viewWillTransition(to: size, with: coordinator)
+
+ if UIDevice.current.orientation.isLandscape {
+ print("Landscape")
+ }
+ if UIDevice.current.orientation.isFlat {
+ print("Flat")
+ } else {
+ print("Portrait")
+ }
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
+
diff --git a/iOSClient/Settings/InitialPrivacySettingsViewController.swift b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
new file mode 100644
index 0000000000..8ae7b2e186
--- /dev/null
+++ b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
@@ -0,0 +1,171 @@
+//
+// InitialPrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+import UIKit
+import XLForm
+
+class InitialPrivacySettingsViewController: UIViewController {
+
+ @IBOutlet weak var dataPrivacyImage: UIImageView!
+ @IBOutlet weak var acceptButton: UIButton!
+ @IBOutlet weak var privacySettingsHelpText: UITextView!
+ @IBOutlet weak var privacySettingsTitle: UILabel!
+ @IBOutlet weak var widthPriavacyHelpView: NSLayoutConstraint!
+ var privacyHelpText = ""
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ privacySettingsTitle.text = NSLocalizedString("_privacy_settings_title_", comment: "")
+ privacyHelpText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ privacySettingsHelpText.text = privacyHelpText
+ dataPrivacyImage.image = UIImage(named: "dataPrivacy")!.image(color: NCBrandColor.shared.brand, size: 60)
+ privacySettingsHelpText.delegate = self
+ privacySettingsHelpText.textColor = .label
+ privacySettingsHelpText.hyperLink(originalText: privacyHelpText,
+ linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): LinkType.privacyPolicy.rawValue,
+ NSLocalizedString("_key_reject_help_", comment: ""): LinkType.reject.rawValue,
+ NSLocalizedString("_key_settings_help_", comment: ""): LinkType.settings.rawValue])
+
+ acceptButton.backgroundColor = NCBrandColor.shared.brand
+ acceptButton.tintColor = UIColor.white
+ acceptButton.layer.cornerRadius = 5
+ acceptButton.layer.borderWidth = 1
+ acceptButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+ acceptButton.setTitle(NSLocalizedString("_accept_button_title_", comment: ""), for: .normal)
+ privacySettingsHelpText.centerText()
+ privacySettingsHelpText.font = UIFont(name: privacySettingsHelpText.font!.fontName, size: 16)
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ self.navigationController?.navigationBar.isHidden = true
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ self.navigationController?.navigationBar.isHidden = false
+ }
+
+ override func viewDidLayoutSubviews(){
+ if UIDevice.current.userInterfaceIdiom == .pad {
+ widthPriavacyHelpView.constant = UIScreen.main.bounds.width - 100
+ }
+ }
+
+ @IBAction func onAcceptButtonClicked(_ sender: Any) {
+ requestPermission()
+ }
+
+ //NEWLY ADDED PERMISSIONS FOR iOS 14
+ func requestPermission() {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization { status in
+ switch status {
+ case .authorized:
+ // Tracking authorization dialog was shown
+ // and we are authorized
+ print("Authorized")
+ // Now that we are authorized we can get the IDFA
+ print(ASIdentifierManager.shared().advertisingIdentifier)
+ case .denied:
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ print("Denied")
+ case .notDetermined:
+ // Tracking authorization dialog has not been shown
+ print("Not Determined")
+ case .restricted:
+ print("Restricted")
+ @unknown default:
+ print("Unknown")
+ }
+ }
+ } else {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+ self.dismiss(animated: true, completion: nil)
+ }
+}
+// MARK: - UITextViewDelegate
+extension InitialPrivacySettingsViewController: UITextViewDelegate {
+ func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
+ if let linkType = LinkType(rawValue: URL.absoluteString) {
+ // TODO: handle linktype here with switch or similar.
+ switch linkType {
+ case LinkType.privacyPolicy:
+ //let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let privacyViewController = PrivacyPolicyViewController()
+ self.navigationController?.pushViewController(privacyViewController, animated: true)
+ case LinkType.reject:
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ self.dismiss(animated: true, completion: nil)
+ case LinkType.settings:
+ let privacySettingsViewController = PrivacySettingsViewController()
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ self.navigationController?.pushViewController(privacySettingsViewController, animated: true)
+ }
+ print("handle link:: \(linkType)")
+ }
+ return false
+ }
+}
+
+public extension UITextView {
+
+ func hyperLink(originalText: String, linkTextsAndTypes: [String: String]) {
+
+ let style = NSMutableParagraphStyle()
+ style.alignment = .left
+
+ let attributedOriginalText = NSMutableAttributedString(string: originalText)
+
+ let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.label, range: fullRange)
+ for linkTextAndType in linkTextsAndTypes {
+ let linkRange = attributedOriginalText.mutableString.range(of: linkTextAndType.key)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: linkTextAndType.value, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: NCBrandColor.shared.brand, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
+ }
+
+ self.linkTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.brand]
+ self.attributedText = attributedOriginalText
+ }
+
+ func centerText() {
+ self.textAlignment = .justified
+ let fittingSize = CGSize(width: 300, height: CGFloat.greatestFiniteMagnitude)
+ let size = sizeThatFits(fittingSize)
+ let topOffset = (bounds.size.height - size.height * zoomScale) / 2
+ let positiveTopOffset = max(1, topOffset)
+ contentOffset.y = -positiveTopOffset
+ }
+}
+
+enum LinkType: String {
+ case reject
+ case privacyPolicy
+ case settings
+}
+
+
diff --git a/iOSClient/Settings/MagentaCloudVersionView.swift b/iOSClient/Settings/MagentaCloudVersionView.swift
new file mode 100644
index 0000000000..925d4a8f50
--- /dev/null
+++ b/iOSClient/Settings/MagentaCloudVersionView.swift
@@ -0,0 +1,32 @@
+//
+// MagentaCloudVersionView.swift
+// Nextcloud
+//
+// Created by A200073704 on 11/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+import XLForm
+
+class MagentaCloudVersionView: XLFormBaseCell{
+
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var versionLabel: UILabel!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+}
diff --git a/iOSClient/Settings/MagentaCloudVersionView.xib b/iOSClient/Settings/MagentaCloudVersionView.xib
new file mode 100644
index 0000000000..9c9d06adfe
--- /dev/null
+++ b/iOSClient/Settings/MagentaCloudVersionView.xib
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/NCManageAutoUploadFileName.swift b/iOSClient/Settings/NCManageAutoUploadFileName.swift
new file mode 100644
index 0000000000..4a192b80f8
--- /dev/null
+++ b/iOSClient/Settings/NCManageAutoUploadFileName.swift
@@ -0,0 +1,238 @@
+//
+// NCManageAutoUploadFileName.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 19/07/17.
+// Copyright (c) 2017 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import Photos
+import NextcloudKit
+import XLForm
+
+class NCManageAutoUploadFileName: XLFormViewController {
+
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let dateExample = Date()
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section Mode filename
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_mode_filename_", comment: ""))
+ form.addFormSection(section)
+
+ // Maintain the original fileName
+
+ row = XLFormRowDescriptor(tag: "maintainOriginalFileName", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_maintain_original_filename_", comment: ""))
+ row.value = NCKeychain().getOriginalFileName(key: NCGlobal.shared.keyFileNameOriginalAutoUpload)
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+
+ row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textLabel.textColor"] = UIColor.label
+
+ section.addFormRow(row)
+
+ // Add File Name Type
+
+ row = XLFormRowDescriptor(tag: "addFileNameType", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_add_filenametype_", comment: ""))
+ row.value = NCKeychain().getFileNameType(key: NCGlobal.shared.keyFileNameAutoUploadType)
+ row.hidden = "$\("maintainOriginalFileName") == 1"
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+ row.cellConfig["switchControl.onTintColor"] = NCBrandColor.shared.brand;
+
+ row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textLabel.textColor"] = UIColor.label
+
+ section.addFormRow(row)
+
+ // Section: Rename File Name
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: ""))
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "maskFileName", rowType: XLFormRowDescriptorTypeText, title: (NSLocalizedString("_filename_", comment: "")))
+
+ let fileNameMask: String = NCKeychain().getFileNameMask(key: NCGlobal.shared.keyFileNameAutoUploadMask)
+ if !fileNameMask.isEmpty {
+ row.cellConfig["textField.text"] = fileNameMask
+ row.value = fileNameMask
+ }else{
+ let placeholderText = CCUtility.createFileName("IMG_0001.JPG", fileDate: dateExample, fileType: PHAssetMediaType.image, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)
+ row.cellConfig["textField.text"] = placeholderText
+ row.value = ""
+ }
+ row.hidden = "$\("maintainOriginalFileName") == 1"
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+
+ row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textLabel.textColor"] = UIColor.label
+
+ row.cellConfig["textField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["textField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["textField.textColor"] = UIColor.label
+
+ section.addFormRow(row)
+
+ // Section: Preview File Name
+
+ row = XLFormRowDescriptor(tag: "previewFileName", rowType: XLFormRowDescriptorTypeTextView, title: "")
+ row.height = 180
+ row.disabled = true
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+
+ row.cellConfig["textView.backgroundColor"] = UIColor.secondarySystemGroupedBackground
+ row.cellConfig["textView.font"] = UIFont.systemFont(ofSize: 14.0)
+ row.cellConfig["textView.textColor"] = UIColor.label
+
+ section.addFormRow(row)
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+ self.form = form
+ }
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.title = NSLocalizedString("_mode_filename_", comment: "")
+ view.backgroundColor = .systemGroupedBackground
+
+ tableView.backgroundColor = .systemGroupedBackground
+
+ initializeForm()
+ reloadForm()
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ // Re-evaluate in-app messages after viewDidAppear
+ MoEngageAnalytics.shared.displayInAppNotificationSafely(reason: "viewDidAppear")
+
+ appDelegate.activeViewController = self
+ }
+
+ // MARK: XLForm
+
+ func reloadForm() {
+
+ self.form.delegate = nil
+
+ let maskFileName: XLFormRowDescriptor = self.form.formRow(withTag: "maskFileName")!
+ let previewFileName: XLFormRowDescriptor = self.form.formRow(withTag: "previewFileName")!
+ previewFileName.value = self.previewFileName(valueRename: maskFileName.value as? String)
+ let placeholderText = CCUtility.createFileName("IMG_0001.JPG", fileDate: dateExample, fileType: PHAssetMediaType.image, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)
+ let fileNameMask : String = NCKeychain().getFileNameMask(key: NCGlobal.shared.keyFileNameAutoUploadMask)
+ if fileNameMask.count > 0 {
+ maskFileName.cellConfig["textField.text"] = fileNameMask
+ }else {
+ maskFileName.cellConfig["textField.text"] = placeholderText
+ }
+ self.tableView.reloadData()
+ self.form.delegate = self
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "addFileNameType" {
+ NCKeychain().setFileNameType(key: NCGlobal.shared.keyFileNameAutoUploadType, prefix: (formRow.value! as AnyObject).boolValue)
+ self.reloadForm()
+ } else if formRow.tag == "maintainOriginalFileName" {
+ NCKeychain().setOriginalFileName(key: NCGlobal.shared.keyFileNameOriginalAutoUpload, value: (formRow.value! as AnyObject).boolValue)
+ self.reloadForm()
+ } else if formRow.tag == "maskFileName" {
+
+ let fileName = formRow.value as? String
+
+ self.form.delegate = nil
+
+ if let fileName = fileName {
+ formRow.value = NCUtility().removeForbiddenCharacters(fileName)
+ formRow.value = FileAutoRenamer.rename(fileName, account: appDelegate.account)
+ }
+
+ self.form.delegate = self
+
+ let previewFileName: XLFormRowDescriptor = self.form.formRow(withTag: "previewFileName")!
+ previewFileName.value = self.previewFileName(valueRename: formRow.value as? String)
+
+ // reload cell
+ if fileName != nil {
+
+ if newValue as? String != formRow.value as? String {
+
+ self.reloadFormRow(formRow)
+
+ let errorDescription = String(format: NSLocalizedString("_forbidden_characters_", comment: ""), NCGlobal.shared.forbiddenCharacters.joined(separator: " "))
+ let error = NKError(errorCode: NCGlobal.shared.errorConflict, errorDescription: errorDescription)
+ NCContentPresenter().showInfo(error: error)
+ }
+ }
+
+ self.reloadFormRow(previewFileName)
+ }
+ }
+
+ // MARK: - Utility
+
+ func previewFileName(valueRename: String?) -> String {
+
+ var returnString: String = ""
+
+ if NCKeychain().getOriginalFileName(key: NCGlobal.shared.keyFileNameOriginalAutoUpload) {
+
+ return (NSLocalizedString("_filename_", comment: "") + ": IMG_0001.JPG")
+
+ } else if let valueRename = valueRename {
+
+ let valueRenameTrimming = valueRename.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
+
+ if valueRenameTrimming.isEmpty {
+
+ NCKeychain().setFileNameMask(key: NCGlobal.shared.keyFileNameAutoUploadMask, mask: "")
+ returnString = CCUtility.createFileName("IMG_0001.JPG", fileDate: dateExample, fileType: PHAssetMediaType.image, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)
+
+ } else {
+
+ self.form.delegate = nil
+ NCKeychain().setFileNameMask(key: NCGlobal.shared.keyFileNameAutoUploadMask, mask: valueRename)
+ self.form.delegate = self
+
+ returnString = CCUtility.createFileName("IMG_0001.JPG", fileDate: dateExample, fileType: PHAssetMediaType.image, keyFileName: NCGlobal.shared.keyFileNameAutoUploadMask, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)
+ }
+ } else {
+
+ NCKeychain().setFileNameMask(key: NCGlobal.shared.keyFileNameAutoUploadMask, mask: "")
+ returnString = CCUtility.createFileName("IMG_0001.JPG", fileDate: dateExample, fileType: PHAssetMediaType.image, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)
+ }
+
+ return String(format: NSLocalizedString("_preview_filename_", comment: ""), "MM,MMM,DD,YY,YYYY and HH,hh,mm,ss,ampm") + ":" + "\n\n" + returnString
+ }
+}
diff --git a/iOSClient/Settings/NCManageE2EE.swift b/iOSClient/Settings/NCManageE2EE.swift
new file mode 100644
index 0000000000..6cab2b6839
--- /dev/null
+++ b/iOSClient/Settings/NCManageE2EE.swift
@@ -0,0 +1,374 @@
+//
+// NCManageE2EE.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 17/11/22.
+// Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import SwiftUI
+import NextcloudKit
+import TOPasscodeViewController
+import LocalAuthentication
+
+@objc class NCManageE2EEInterface: NSObject {
+
+ @objc func makeShipDetailsUI(account: String) -> UIViewController {
+
+ let details = NCViewE2EE(account: account)
+ let vc = UIHostingController(rootView: details)
+ vc.title = NSLocalizedString("_e2e_settings_", comment: "")
+ return vc
+ }
+}
+
+class NCManageE2EE: NSObject, ObservableObject, NCEndToEndInitializeDelegate, TOPasscodeViewControllerDelegate {
+
+ let endToEndInitialize = NCEndToEndInitialize()
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ var passcodeType = ""
+
+ @Published var isEndToEndEnabled: Bool = false
+ @Published var statusOfService: String = NSLocalizedString("_status_in_progress_", comment: "")
+
+ override init() {
+ super.init()
+
+ endToEndInitialize.delegate = self
+ isEndToEndEnabled = NCKeychain().isEndToEndEnabled(account: appDelegate.account)
+ if isEndToEndEnabled {
+ statusOfService = NSLocalizedString("_status_e2ee_configured_", comment: "")
+ } else {
+ endToEndInitialize.statusOfService { error in
+ if error == .success {
+ self.statusOfService = NSLocalizedString("_status_e2ee_on_server_", comment: "")
+ } else {
+ self.statusOfService = NSLocalizedString("_status_e2ee_not_setup_", comment: "")
+ }
+ }
+ }
+ }
+
+ // MARK: - Delegate
+
+ func endToEndInitializeSuccess() {
+ isEndToEndEnabled = true
+ }
+
+ // MARK: - Passcode
+
+ @objc func requestPasscodeType(_ passcodeType: String) {
+
+ let laContext = LAContext()
+ var error: NSError?
+
+ let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: true)
+ passcodeViewController.delegate = self
+ passcodeViewController.keypadButtonShowLettering = false
+ if NCKeychain().touchFaceID, laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
+ if error == nil {
+ if laContext.biometryType == .faceID {
+ passcodeViewController.biometryType = .faceID
+ passcodeViewController.allowBiometricValidation = true
+ } else if laContext.biometryType == .touchID {
+ passcodeViewController.biometryType = .touchID
+ }
+ passcodeViewController.allowBiometricValidation = true
+ passcodeViewController.automaticallyPromptForBiometricValidation = true
+ }
+ }
+
+ self.passcodeType = passcodeType
+ appDelegate.window?.rootViewController?.present(passcodeViewController, animated: true)
+ }
+
+ @objc func correctPasscode() {
+
+ switch self.passcodeType {
+ case "startE2E":
+ endToEndInitialize.initEndToEndEncryption()
+ case "readPassphrase":
+ if let e2ePassphrase = NCKeychain().getEndToEndPassphrase(account: appDelegate.account) {
+ print("[INFO]Passphrase: " + e2ePassphrase)
+ let message = "\n" + NSLocalizedString("_e2e_settings_the_passphrase_is_", comment: "") + "\n\n\n" + e2ePassphrase
+ let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_copy_passphrase_", comment: ""), style: .default, handler: { _ in
+ UIPasteboard.general.string = e2ePassphrase
+ }))
+ appDelegate.window?.rootViewController?.present(alertController, animated: true)
+ }
+ case "removeLocallyEncryption":
+ let alertController = UIAlertController(title: NSLocalizedString("_e2e_settings_remove_", comment: ""), message: NSLocalizedString("_e2e_settings_remove_message_", comment: ""), preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_", comment: ""), style: .default, handler: { _ in
+ NCKeychain().clearAllKeysEndToEnd(account: self.appDelegate.account)
+ self.isEndToEndEnabled = NCKeychain().isEndToEndEnabled(account: self.appDelegate.account)
+ }))
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .default, handler: { _ in }))
+ appDelegate.window?.rootViewController?.present(alertController, animated: true)
+ default:
+ break
+ }
+ }
+
+ func passcodeViewController(_ passcodeViewController: TOPasscodeViewController, isCorrectCode code: String) -> Bool {
+
+ if code == NCKeychain().passcode {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.correctPasscode()
+ }
+ return true
+ } else {
+ return false
+ }
+ }
+
+ func didPerformBiometricValidationRequest(in passcodeViewController: TOPasscodeViewController) {
+
+ LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { success, _ in
+ if success {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ passcodeViewController.dismiss(animated: true)
+ self.correctPasscode()
+ }
+ }
+ }
+ }
+
+ func didTapCancel(in passcodeViewController: TOPasscodeViewController) {
+ passcodeViewController.dismiss(animated: true)
+ }
+}
+
+// MARK: Views
+
+struct NCViewE2EE: View {
+
+ @ObservedObject var manageE2EE = NCManageE2EE()
+ @State var account: String = ""
+
+ var body: some View {
+
+ VStack {
+
+ if manageE2EE.isEndToEndEnabled {
+
+ List {
+
+ Section(header: Text(""), footer: Text("End-to-End Encryption " + NCGlobal.shared.capabilityE2EEApiVersion)) {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_activated_", comment: ""))
+ } icon: {
+ Image(systemName: "checkmark.circle.fill")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(.green)
+ }
+ }
+
+ Section(header: Text(""), footer: Text(NSLocalizedString("_read_passphrase_description_", comment: ""))) {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_read_passphrase_", comment: ""))
+ } icon: {
+ Image(systemName: "eye")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(Color(NCBrandColor.shared.iconColor))
+ .frame(width: 20, height: 30)
+ }
+ .onTapGesture {
+ if NCKeychain().passcode != nil {
+ manageE2EE.requestPasscodeType("readPassphrase")
+ } else {
+ NCContentPresenter().showInfo(error: NKError(errorCode: 0, errorDescription: "_e2e_settings_lock_not_active_"))
+ }
+ }
+ }
+
+ let removeStrDesc1 = NSLocalizedString("_remove_passphrase_desc_1_", comment: "")
+ let removeStrDesc2 = NSLocalizedString("_remove_passphrase_desc_2_", comment: "")
+ let removeStrDesc = String(format: "%@\n\n%@", removeStrDesc1, removeStrDesc2)
+ Section(header: Text(""), footer: Text(removeStrDesc)) {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_remove_", comment: ""))
+ } icon: {
+ Image(systemName: "trash")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(Color(NCBrandColor.shared.iconColor))
+ .frame(width: 20, height: 30)
+ }
+ .onTapGesture {
+ if NCKeychain().passcode != nil {
+ manageE2EE.requestPasscodeType("removeLocallyEncryption")
+ } else {
+ NCContentPresenter().showInfo(error: NKError(errorCode: 0, errorDescription: "_e2e_settings_lock_not_active_"))
+ }
+ }
+ }
+#if DEBUG
+ DeleteCerificateSection()
+#endif
+ }
+
+ } else {
+
+ List {
+ let startE2EDesc1 = NSLocalizedString("_start_e2e_encryption_1_", comment: "");
+ let startE2EDesc2 = NSLocalizedString("_start_e2e_encryption_2_", comment: "");
+ let startE2EDesc3 = NSLocalizedString("_start_e2e_encryption_3_", comment: "");
+ let startE2EDesc = String(format: "%@\n\n%@\n\n%@",startE2EDesc1,startE2EDesc2,startE2EDesc3)
+ Section(header: Text(""), footer: Text(startE2EDesc)) {
+ HStack {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_start_", comment: ""))
+ } icon: {
+ }
+ Spacer()
+ }
+ .contentShape(Rectangle())
+ .onTapGesture {
+ if let passcode = NCKeychain().passcode {
+ manageE2EE.requestPasscodeType("startE2E")
+ } else {
+ NCContentPresenter().showInfo(error: NKError(errorCode: 0, errorDescription: "_e2e_settings_lock_not_active_"))
+ }
+ }
+ }
+
+#if DEBUG
+ DeleteCerificateSection()
+#endif
+ }
+ .listStyle(GroupedListStyle())
+ }
+ }
+ .background(Color(UIColor.systemGroupedBackground))
+ }
+}
+
+struct DeleteCerificateSection: View {
+
+ var body: some View {
+
+ Section(header: Text("Delete Server keys"), footer: Text("Available only in debug mode")) {
+
+ HStack {
+ Label {
+ Text("Delete Certificate")
+ } icon: {
+ Image(systemName: "exclamationmark.triangle")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(Color(UIColor.systemGray))
+ }
+ Spacer()
+ }
+ .contentShape(Rectangle())
+ .onTapGesture {
+ NextcloudKit.shared.deleteE2EECertificate { _, error in
+ if error == .success {
+ NCContentPresenter().messageNotification("E2E delete certificate", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .success)
+ } else {
+ NCContentPresenter().messageNotification("E2E delete certificate", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .error)
+ }
+ }
+ }
+
+ HStack {
+ Label {
+ Text("Delete PrivateKey")
+ } icon: {
+ Image(systemName: "exclamationmark.triangle")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(Color(UIColor.systemGray))
+ }
+ Spacer()
+ }
+ .contentShape(Rectangle())
+ .onTapGesture {
+ NextcloudKit.shared.deleteE2EEPrivateKey { _, error in
+ if error == .success {
+ NCContentPresenter().messageNotification("E2E delete privateKey", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .success)
+ } else {
+ NCContentPresenter().messageNotification("E2E delete privateKey", error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .error)
+ }
+ }
+ }
+ }
+ }
+}
+
+// MARK: - Preview / Test
+
+struct SectionView: View {
+
+ @State var height: CGFloat = 0
+ @State var text: String = ""
+
+ var body: some View {
+ HStack {
+ Text(text)
+ }
+ .frame(maxWidth: .infinity, minHeight: height, alignment: .bottomLeading)
+ }
+}
+
+struct NCViewE2EETest: View {
+
+ var body: some View {
+
+ VStack {
+ List {
+ Section(header: SectionView(height: 50, text: "Section Header View")) {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_activated_", comment: ""))
+ } icon: {
+ Image(systemName: "checkmark.circle.fill")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 25, height: 25)
+ .foregroundColor(.green)
+ }
+ }
+ Section(header: SectionView(text: "Section Header View 42")) {
+ Label {
+ Text(NSLocalizedString("_e2e_settings_activated_", comment: ""))
+ } icon: {
+ Image(systemName: "checkmark.circle.fill")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 25, height: 25)
+ .foregroundColor(.red)
+ }
+ }
+ }
+ }
+ }
+}
+
+struct NCViewE2EE_Previews: PreviewProvider {
+ static var previews: some View {
+
+ // swiftlint:disable force_cast
+ let account = (UIApplication.shared.delegate as! AppDelegate).account
+ NCViewE2EE(account: account)
+ // swiftlint:enable force_cast
+ }
+}
diff --git a/iOSClient/Settings/NCSettings.h b/iOSClient/Settings/NCSettings.h
new file mode 100644
index 0000000000..e06a29f1ac
--- /dev/null
+++ b/iOSClient/Settings/NCSettings.h
@@ -0,0 +1,33 @@
+//
+// NCSettings.h
+// Nextcloud
+//
+// Created by Marino Faggiana on 24/11/14.
+// Copyright (c) 2014 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import
+#import
+#import
+#import "CCUtility.h"
+
+@interface NCSettings : XLFormViewController
+
+- (void)reloadForm;
+
+@end
diff --git a/iOSClient/Settings/NCSettings.m b/iOSClient/Settings/NCSettings.m
new file mode 100644
index 0000000000..e416450e70
--- /dev/null
+++ b/iOSClient/Settings/NCSettings.m
@@ -0,0 +1,436 @@
+//
+// NCSettings.m
+// Nextcloud
+//
+// Created by Marino Faggiana on 24/11/14.
+// Copyright (c) 2014 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+#import "NCSettings.h"
+#import "CCAdvanced.h"
+#import "CCManageAccount.h"
+#import "CCManageAutoUpload.h"
+#import "NCBridgeSwift.h"
+#import
+#import
+//#import
+
+#define alertViewEsci 1
+#define alertViewAzzeraCache 2
+
+@interface NCSettings ()
+{
+ AppDelegate *appDelegate;
+ TOPasscodeViewController *passcodeViewController;
+ TOPasscodeSettingsViewController *passcodeSettingsViewController;
+
+ NSString *versionServer;
+ NSString *themingName;
+ NSString *themingSlogan;
+}
+@end
+
+@implementation NCSettings
+
+- (void)initializeForm
+{
+ XLFormDescriptor *form = [XLFormDescriptor formDescriptor];
+ XLFormSectionDescriptor *section;
+ XLFormRowDescriptor *row;
+
+ form.rowNavigationOptions = XLFormRowNavigationOptionNone;
+
+ // Section AUTO UPLOAD OF CAMERA IMAGES ----------------------------
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUpload" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_settings_autoupload_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"autoUpload"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ row.action.viewControllerClass = [CCManageAutoUpload class];
+ [section addFormRow:row];
+
+ // Section : SECURITY --------------------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_security_", nil)];
+ [form addFormSection:section];
+
+ // Lock active YES/NO
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"bloccopasscode" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_lock_not_active_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[[UIImage imageNamed:@"lock_open"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ //[row.cellConfig setObject:@(UITableViewCellAccessoryDisclosureIndicator) forKey:@"accessoryType"];
+ row.action.formSelector = @selector(passcode:);
+ [section addFormRow:row];
+ // Enable Touch ID
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"enableTouchDaceID" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_enable_touch_face_id_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.cellConfig[@"switchControl.onTintColor"] = NCBrandColor.shared.brand;
+ if([[NCKeychain alloc] init].passcode){
+ row.disabled = @NO;
+ }else{
+ row.disabled = @YES;
+ }
+ [section addFormRow:row];
+
+ // Section : E2EEncryption --------------------------------------------------------------
+
+ BOOL isE2EEEnabled = [[NCGlobal shared] capabilityE2EEEnabled];
+ NSString *versionE2EE = [[NCGlobal shared] capabilityE2EEApiVersion];
+
+ if (isE2EEEnabled == YES && [NCGlobal.shared.e2eeCompatibleVersions containsObject:versionE2EE]) {
+
+ // EndToEnd Encryption
+ NSString *title = [NSString stringWithFormat:@"%@",NSLocalizedString(@"_e2e_settings_", nil)];
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"e2eEncryption" rowType:XLFormRowDescriptorTypeButton title:title];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ row.action.formSelector = @selector(manageE2EE:);
+ [section addFormRow:row];
+ }
+
+ // Section Advanced -------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSection];
+ [form addFormSection:section];
+
+ // Advanced
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"advanced" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_advanced_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:[[UIImage imageNamed:@"gear"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ row.action.viewControllerClass = [CCAdvanced class];
+ [section addFormRow:row];
+
+ // Section : DATA PROTECTION ------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_data_protection_", nil)];
+ [form addFormSection:section];
+
+ // Privacy Settings
+ PrivacySettingsViewController *privacySettingsViewController = [[PrivacySettingsViewController alloc] init];
+ privacySettingsViewController.isShowSettingsButton = true;
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"showSettingsButton"];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"privacySettings" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_privacy_settings_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.viewControllerClass = [privacySettingsViewController class];
+ [section addFormRow:row];
+
+ // Privacy Policy
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"advanced" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_privacy_policy_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ row.action.viewControllerClass = [PrivacyPolicyViewController class];
+ [section addFormRow:row];
+
+ // Used OpenSource Software
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"buttonLeftAligned" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_used_opensource_software_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.viewControllerClass = [OpenSourceSoftwareViewController class];
+ [section addFormRow:row];
+
+
+ // Section : SERVICE ------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_service_", nil)];
+ [form addFormSection:section];
+
+ // HELP
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"buttonLeftAligned" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_help_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.viewControllerClass = [HelpViewController class];
+ [section addFormRow:row];
+
+ // Imprint
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"buttonLeftAligned" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_imprint_", nil)];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+ [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+ row.action.viewControllerClass = [ImprintViewController class];
+ [section addFormRow:row];
+
+ // Section : INFO ------------------------------------------------
+
+ section = [XLFormSectionDescriptor formSectionWithTitle:NSLocalizedString(@"_info_", nil)];
+ [form addFormSection:section];
+
+ //MagentaCloud Version
+
+ //custom cell
+
+ [[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MagentaCloudVersionView class] forKey:@"kNMCCustomCellType"];
+
+ row = [XLFormRowDescriptor formRowDescriptorWithTag:@"disablefilesapp" rowType:@"kNMCCustomCellType" title:NSLocalizedString(@"_magentacloud_version_", nil)];
+ NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleInfoDictionaryVersion"];
+ row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+ row.cellConfigAtConfigure[@"cellLabel.text"] = NSLocalizedString(@"_magentacloud_version_", nil);
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"cellLabel.font"];
+ [row.cellConfig setObject:UIColor.labelColor forKey:@"cellLabel.textColor"];
+ row.cellConfigAtConfigure[@"versionLabel.text"] = appVersion;
+ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"versionLabel.font"];
+ [row.cellConfig setObject:UIColor.systemGrayColor forKey:@"versionLabel.textColor"];
+ [section addFormRow:row];
+
+ self.tableView.showsVerticalScrollIndicator = NO;
+ self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 35, 0);
+ self.form = form;
+}
+
+// MARK: - View Life Cycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.title = NSLocalizedString(@"_settings_", nil);
+ self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
+ self.tableView.backgroundColor = UIColor.systemGroupedBackgroundColor;
+ appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:NCGlobal.shared.notificationCenterApplicationDidEnterBackground object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeUser) name:NCGlobal.shared.notificationCenterInitialize object:nil];
+
+ [self initializeForm];
+ [self reloadForm];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ appDelegate.activeViewController = self;
+
+ versionServer = [[NCGlobal shared] capabilityServerVersion];
+ themingName = [[NCGlobal shared] capabilityThemingName];
+ themingSlogan = [[NCGlobal shared] capabilityThemingSlogan];
+}
+
+#pragma mark - NotificationCenter
+
+- (void)changeUser
+{
+ [self initializeForm];
+ [self reloadForm];
+}
+
+- (void)applicationDidEnterBackground
+{
+ if (passcodeViewController.view.window != nil) {
+ [passcodeViewController dismissViewControllerAnimated:true completion:nil];
+ }
+ if (passcodeSettingsViewController.view.window != nil) {
+ [passcodeSettingsViewController dismissViewControllerAnimated:true completion:nil];
+ }
+
+ [[self navigationController] popToRootViewControllerAnimated:false];
+}
+
+#pragma mark -
+
+- (void)reloadForm
+{
+ self.form.delegate = nil;
+
+ // ------------------------------------------------------------------
+
+ XLFormRowDescriptor *rowBloccoPasscode = [self.form formRowWithTag:@"bloccopasscode"];
+ XLFormRowDescriptor *rowNotPasscodeAtStart = [self.form formRowWithTag:@"notPasscodeAtStart"];
+ XLFormRowDescriptor *rowEnableTouchDaceID = [self.form formRowWithTag:@"enableTouchDaceID"];
+
+ // ------------------------------------------------------------------
+
+ if ([[NCKeychain alloc] init].passcode) {
+ rowBloccoPasscode.title = NSLocalizedString(@"_lock_active_", nil);
+ [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ rowEnableTouchDaceID.disabled = @NO;
+ rowNotPasscodeAtStart.disabled = @NO;
+ } else {
+ rowBloccoPasscode.title = NSLocalizedString(@"_lock_not_active_", nil);
+ [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock_open"] imageWithColor:NCBrandColor.shared.iconImageColor size:25] forKey:@"imageView.image"];
+ rowEnableTouchDaceID.disabled = @YES;
+ rowNotPasscodeAtStart.disabled = @YES;
+ }
+
+ if ([[NCKeychain alloc] init].touchFaceID) [rowEnableTouchDaceID setValue:@1]; else [rowEnableTouchDaceID setValue:@0];
+ if ([[NCKeychain alloc] init].requestPasscodeAtStart) [rowNotPasscodeAtStart setValue:@0]; else [rowNotPasscodeAtStart setValue:@1];
+
+
+ // -----------------------------------------------------------------
+
+ [self.tableView reloadData];
+
+ self.form.delegate = self;
+}
+
+- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
+{
+ [super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
+
+ if ([rowDescriptor.tag isEqualToString:@"notPasscodeAtStart"]) {
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+ [[NCKeychain alloc] init].requestPasscodeAtStart = false;
+ } else {
+ [[NCKeychain alloc] init].requestPasscodeAtStart = true;
+ }
+ }
+
+ if ([rowDescriptor.tag isEqualToString:@"enableTouchDaceID"]) {
+
+ if ([[rowDescriptor.value valueData] boolValue] == YES) {
+ [[NCKeychain alloc] init].touchFaceID = true;
+ } else {
+ [[NCKeychain alloc] init].touchFaceID = false;
+ }
+ }
+}
+
+#pragma mark -
+
+- (void)manageE2EE:(XLFormRowDescriptor *)sender
+{
+ [self deselectFormRow:sender];
+
+ UIViewController *vc = [[NCManageE2EEInterface alloc] makeShipDetailsUIWithAccount:appDelegate.account];
+ [self.navigationController pushViewController:vc animated:YES];
+}
+
+#pragma mark - Passcode
+
+- (void)didPerformBiometricValidationRequestInPasscodeViewController:(TOPasscodeViewController *)passcodeViewController
+{
+ [[LAContext new] evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:[[NCBrandOptions shared] brand] reply:^(BOOL success, NSError * _Nullable error) {
+ if (success) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+ [[NCKeychain alloc] init].passcode = nil;
+ [passcodeViewController dismissViewControllerAnimated:YES completion:nil];
+ [self reloadForm];
+ });
+ }
+ }];
+}
+
+- (void)passcodeSettingsViewController:(TOPasscodeSettingsViewController *)passcodeSettingsViewController didChangeToNewPasscode:(NSString *)passcode ofType:(TOPasscodeType)type
+{
+ [[NCKeychain alloc] init].passcode = passcode;
+ [passcodeSettingsViewController dismissViewControllerAnimated:YES completion:nil];
+
+ [self reloadForm];
+}
+
+- (void)didTapCancelInPasscodeViewController:(TOPasscodeViewController *)passcodeViewController
+{
+ [passcodeViewController dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (BOOL)passcodeViewController:(TOPasscodeViewController *)passcodeViewController isCorrectCode:(NSString *)code
+{
+ if ([code isEqualToString:[[NCKeychain alloc] init].passcode]) {
+ [[NCKeychain alloc] init].passcode = nil;
+ [self reloadForm];
+
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)passcode:(XLFormRowDescriptor *)sender
+{
+ LAContext *laContext = [LAContext new];
+ NSError *error;
+
+ [self deselectFormRow:sender];
+
+ if ([[NCKeychain alloc] init].passcode) {
+
+ passcodeViewController = [[TOPasscodeViewController alloc] initPasscodeType:TOPasscodeTypeSixDigits allowCancel:true];
+ passcodeViewController.delegate = self;
+ passcodeViewController.keypadButtonShowLettering = false;
+
+ if ([[NCKeychain alloc] init].touchFaceID && [laContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
+ if (error == NULL) {
+ if (laContext.biometryType == LABiometryTypeFaceID) {
+ passcodeViewController.biometryType = TOPasscodeBiometryTypeFaceID;
+ passcodeViewController.allowBiometricValidation = true;
+ passcodeViewController.automaticallyPromptForBiometricValidation = true;
+ } else if (laContext.biometryType == LABiometryTypeTouchID) {
+ passcodeViewController.biometryType = TOPasscodeBiometryTypeTouchID;
+ passcodeViewController.allowBiometricValidation = true;
+ passcodeViewController.automaticallyPromptForBiometricValidation = true;
+ } else {
+ NSLog(@"No Biometric support");
+ }
+ }
+ }
+
+ [self presentViewController:passcodeViewController animated:YES completion:nil];
+
+ } else {
+
+ passcodeSettingsViewController = [[TOPasscodeSettingsViewController alloc] init];
+ passcodeSettingsViewController.hideOptionsButton = YES;
+ passcodeSettingsViewController.requireCurrentPasscode = NO;
+ passcodeSettingsViewController.passcodeType = TOPasscodeTypeSixDigits;
+ passcodeSettingsViewController.delegate = self;
+
+ [self presentViewController:passcodeSettingsViewController animated:YES completion:nil];
+ }
+}
+
+#pragma mark -
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return NCGlobal.shared.heightCellSettings;
+}
+
+- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
+{
+ NSString *sectionName;
+ NSInteger numSections = [tableView numberOfSections] - 1;
+
+ return sectionName;
+}
+
+@end
diff --git a/iOSClient/Settings/NCSettings.storyboard b/iOSClient/Settings/NCSettings.storyboard
new file mode 100644
index 0000000000..19a320515b
--- /dev/null
+++ b/iOSClient/Settings/NCSettings.storyboard
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/OpenSourceSoftwareViewController.swift b/iOSClient/Settings/OpenSourceSoftwareViewController.swift
new file mode 100644
index 0000000000..e4cd131f83
--- /dev/null
+++ b/iOSClient/Settings/OpenSourceSoftwareViewController.swift
@@ -0,0 +1,56 @@
+//
+// OpenSourceSoftwareViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 11/05/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import WebKit
+
+class OpenSourceSoftwareViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view, typically from a nib.
+
+ self.title = NSLocalizedString("_used_opensource_software_", comment: "")
+ let myWebView:WKWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://static.magentacloud.de/licences/ios.html")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
diff --git a/iOSClient/Settings/PrivacyPolicyViewController.swift b/iOSClient/Settings/PrivacyPolicyViewController.swift
new file mode 100644
index 0000000000..9e1bd0ed64
--- /dev/null
+++ b/iOSClient/Settings/PrivacyPolicyViewController.swift
@@ -0,0 +1,66 @@
+//
+// PrivacyPolicyViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+import WebKit
+
+class PrivacyPolicyViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ var myWebView = WKWebView()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.title = NSLocalizedString("_privacy_policy_", comment: "")
+
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://static.magentacloud.de/privacy/datenschutzhinweise_app.htm")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ // Re-evaluate in-app messages after viewDidAppear
+ MoEngageAnalytics.shared.displayInAppNotificationSafely(reason: "viewDidAppear")
+
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ }
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
new file mode 100644
index 0000000000..757d5532d5
--- /dev/null
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -0,0 +1,148 @@
+//
+// PrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+import XLForm
+
+class PrivacySettingsViewController: XLFormViewController{
+
+ @objc public var isShowSettingsButton: Bool = false
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+
+ let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
+ self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
+ isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ changeTheming()
+ }
+
+ @objc func changeTheming() {
+ tableView.backgroundColor = .systemGroupedBackground
+ tableView.separatorColor = .none
+ tableView.separatorColor = .clear
+ tableView.reloadData()
+ initializeForm()
+ }
+
+
+
+ //MARK: XLForm
+
+ func initializeForm() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ //custom cell
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
+ if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
+ }else {
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
+ }
+
+ section.addFormRow(row)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ form.addFormSection(section)
+
+
+ row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
+ row.cellConfig["backgroundColor"] = UIColor.clear
+
+ if(isShowSettingsButton){
+ section.addFormRow(row)
+ }
+
+
+ self.form = form
+ }
+
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "SaveSettingsButton" {
+ print("save settings clicked")
+ //TODO save button state and leave the page
+ self.navigationController?.popViewController(animated: true)
+
+ }
+ if formRow.tag == "AnalysisDataCollectionSwitch"{
+ if (formRow.value! as AnyObject).boolValue {
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
+ if status == .denied {
+ guard let url = URL(string: UIApplication.openSettingsURLString) else {
+ return
+ }
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ })
+ }
+ }
+ UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+
+ }
+
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.swift b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
new file mode 100644
index 0000000000..0d5a34732f
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
@@ -0,0 +1,34 @@
+//
+// RequiredDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+class RequiredDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var requiredDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ //requiredDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+
+ requiredDataCollectionSwitchControl.isOn = true
+ requiredDataCollectionSwitchControl.isEnabled = false
+ }
+
+ override func update() {
+ super.update()
+ }
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.xib b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
new file mode 100644
index 0000000000..66156c6e06
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.swift b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
new file mode 100644
index 0000000000..1332b1f92a
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
@@ -0,0 +1,47 @@
+//
+// SaveSettingsCustomButtonCell.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import XLForm
+
+
+class SaveSettingsCustomButtonCell: XLFormButtonCell {
+
+ @IBOutlet weak var saveSettingsButton: UIButton!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ self.separatorInset = UIEdgeInsets(top: 0, left: .greatestFiniteMagnitude, bottom: 0, right: .greatestFiniteMagnitude)
+ saveSettingsButton.setTitle(NSLocalizedString("_save_settings_", comment: ""), for: .normal)
+ saveSettingsButton.addTarget(self, action: #selector(saveButtonClicked), for: .touchUpInside)
+
+ }
+
+ override func configure() {
+ super.configure()
+ saveSettingsButton.backgroundColor = NCBrandColor.shared.brand
+ saveSettingsButton.tintColor = UIColor.white
+ saveSettingsButton.layer.cornerRadius = 5
+ saveSettingsButton.layer.borderWidth = 1
+ saveSettingsButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+
+ }
+
+ override func update() {
+ super.update()
+
+ }
+
+ @objc func saveButtonClicked(sender: UIButton) {
+ self.rowDescriptor.value = sender
+
+ }
+
+}
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.xib b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
new file mode 100644
index 0000000000..0dd197da31
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/Settings.bundle/Root.plist b/iOSClient/Settings/Settings.bundle/Root.plist
new file mode 100644
index 0000000000..1184bfbc77
--- /dev/null
+++ b/iOSClient/Settings/Settings.bundle/Root.plist
@@ -0,0 +1,31 @@
+
+
+
+
+ StringsTable
+ Root
+ PreferenceSpecifiers
+
+
+ Type
+ PSToggleSwitchSpecifier
+ Title
+ _reset_application_
+ Key
+ reset_application
+ DefaultValue
+
+
+
+ Type
+ PSTitleValueSpecifier
+ Title
+ _version_
+ Key
+ version_preference
+ DefaultValue
+ 0
+
+
+
+
diff --git a/iOSClient/Settings/Settings.bundle/en.lproj/Root.strings b/iOSClient/Settings/Settings.bundle/en.lproj/Root.strings
new file mode 100644
index 0000000000..6b57b356ce
Binary files /dev/null and b/iOSClient/Settings/Settings.bundle/en.lproj/Root.strings differ
diff --git a/iOSClient/Share/NCShareEmailFieldCell.swift b/iOSClient/Share/NCShareEmailFieldCell.swift
new file mode 100644
index 0000000000..48cdcb1774
--- /dev/null
+++ b/iOSClient/Share/NCShareEmailFieldCell.swift
@@ -0,0 +1,188 @@
+//
+// NCShareEmailFieldCell.swift
+// Nextcloud
+//
+// Created by A200020526 on 01/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import MarqueeLabel
+import NextcloudKit
+
+enum Tag {
+ static let searchField = 999
+}
+
+class NCShareEmailFieldCell: UITableViewCell {
+
+ @IBOutlet weak var searchField: UITextField!
+ @IBOutlet weak var labelOrLink: UILabel!
+ @IBOutlet weak var btnContact: UIButton!
+ @IBOutlet weak var labelSeparator1: UILabel!
+ @IBOutlet weak var labelSeparator2: UILabel!
+ @IBOutlet weak var labelSendLinkByMail: UILabel!
+ @IBOutlet weak var labelSharedWithBy: UILabel!
+ @IBOutlet weak var labelResharingAllowed: UILabel!
+ @IBOutlet weak var topConstraintResharingView: NSLayoutConstraint!
+ @IBOutlet weak var viewOrLinkSeparator: UIView!
+
+ var ocId = ""
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+ }
+
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ super.traitCollectionDidChange(previousTraitCollection)
+ if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
+ setupCellAppearance()
+ }
+ }
+
+ func setupCell(with metadata: tableMetadata) {
+// contentView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+ ocId = metadata.ocId
+
+ setupCellAppearance()
+ updateCanReshareUI()
+
+ setNeedsLayout()
+ layoutIfNeeded()
+ }
+
+ func setupCellAppearance() {
+ configureSearchField()
+ configureContactButton()
+ configureLabels()
+ }
+
+ private func configureSearchField() {
+ searchField.layer.cornerRadius = 5
+ searchField.layer.masksToBounds = true
+ searchField.layer.borderWidth = 1
+ searchField.layer.borderColor = NCBrandColor.shared.label.cgColor
+ searchField.text = ""
+ searchField.textColor = NCBrandColor.shared.label
+ searchField.attributedPlaceholder = NSAttributedString(
+ string: NSLocalizedString("_shareLinksearch_placeholder_", comment: ""),
+ attributes: [.foregroundColor: NCBrandColor.shared.gray60]
+ )
+ searchField.tag = Tag.searchField
+ setDoneButton(sender: searchField)
+ }
+
+ private func configureContactButton() {
+ btnContact.layer.cornerRadius = 5
+ btnContact.layer.masksToBounds = true
+ btnContact.layer.borderWidth = 1
+ btnContact.layer.borderColor = NCBrandColor.shared.label.cgColor
+ if let image = UIImage(named: "Contacts")?.withRenderingMode(.alwaysTemplate) {
+ btnContact.setImage(image, for: .normal)
+ }
+ btnContact.tintColor = NCBrandColor.shared.label
+
+ }
+
+ private func configureLabels() {
+ labelOrLink.text = NSLocalizedString("_share_or_", comment: "")
+ labelSendLinkByMail.text = NSLocalizedString("_share_send_link_by_mail_", comment: "")
+ labelSharedWithBy.text = NSLocalizedString("_share_received_shares_text_", comment: "")
+ labelResharingAllowed.text = NSLocalizedString("_share_reshare_allowed_", comment: "")
+
+ labelSendLinkByMail.textColor = NCBrandColor.shared.label
+ labelSharedWithBy.textColor = NCBrandColor.shared.label
+ labelResharingAllowed.textColor = NCBrandColor.shared.label
+ }
+
+ func updateCanReshareUI() {
+ guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return }
+
+ let isCurrentUser = NCShareCommon.isCurrentUserIsFileOwner(fileOwnerId: metadata.ownerId)
+ let canReshare = (metadata.sharePermissionsCollaborationServices & NCPermissions().permissionShareShare) != 0
+
+ labelSharedWithBy.isHidden = isCurrentUser
+ labelResharingAllowed.isHidden = isCurrentUser
+
+ if !canReshare {
+ searchField.isUserInteractionEnabled = false
+ searchField.alpha = 0.5
+ btnContact.isEnabled = false
+ btnContact.alpha = 0.5
+ }
+
+ if !isCurrentUser {
+ let ownerName = metadata.ownerDisplayName
+ let fullText = NSLocalizedString("_share_received_shares_text_", comment: "") + " " + ownerName
+ let attributed = NSMutableAttributedString(string: fullText)
+
+ if let range = fullText.range(of: ownerName) {
+ let nsRange = NSRange(range, in: fullText)
+ attributed.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 16), range: nsRange)
+ }
+
+ labelSharedWithBy.attributedText = attributed
+ labelSharedWithBy.numberOfLines = 0
+
+ labelResharingAllowed.text = canReshare
+ ? NSLocalizedString("_share_reshare_allowed_", comment: "")
+ : NSLocalizedString("_share_reshare_not_allowed_", comment: "")
+
+ topConstraintResharingView.constant = 15
+ } else {
+ topConstraintResharingView.constant = 0
+ }
+
+ viewOrLinkSeparator.isHidden = !canReshare
+ }
+
+ func updateShareUI(ocId: String, count: Int) {
+ guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return }
+
+ let isCurrentUser = NCShareCommon.isCurrentUserIsFileOwner(fileOwnerId: metadata.ownerId)
+ let canReshare = (metadata.sharePermissionsCollaborationServices & NCPermissions().permissionShareShare) != 0
+
+ if !isCurrentUser {
+ if canReshare {
+ labelOrLink.isHidden = true
+ labelSeparator1.isHidden = true
+ labelSeparator2.isHidden = true
+ }
+ }
+ }
+
+ @objc func cancelDatePicker() {
+ self.searchField.endEditing(true)
+ }
+
+ private func setDoneButton(sender: UITextField) {
+ let toolbar = UIToolbar()
+ toolbar.sizeToFit()
+ let doneButton = UIBarButtonItem(
+ title: NSLocalizedString("_done_", comment: ""),
+ style: .plain,
+ target: self,
+ action: #selector(cancelDatePicker)
+ )
+ let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
+ toolbar.setItems([space, doneButton], animated: false)
+ sender.inputAccessoryView = toolbar
+ }
+
+ @IBAction func touchUpInsideFavorite(_ sender: UIButton) {
+ // Hook for favorite action if needed
+ }
+
+ @IBAction func touchUpInsideDetails(_ sender: UIButton) {
+ // Hook for toggling detail visibility if needed
+ }
+
+ @objc func longTap(_ sender: UIGestureRecognizer) {
+ let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_copied_path_")
+ NCContentPresenter().showInfo(error: error)
+ }
+}
diff --git a/iOSClient/Share/NCShareEmailFieldCell.xib b/iOSClient/Share/NCShareEmailFieldCell.xib
new file mode 100644
index 0000000000..8384397621
--- /dev/null
+++ b/iOSClient/Share/NCShareEmailFieldCell.xib
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Share/ShareDownloadLimitNetwork.swift b/iOSClient/Share/ShareDownloadLimitNetwork.swift
new file mode 100644
index 0000000000..06858464bf
--- /dev/null
+++ b/iOSClient/Share/ShareDownloadLimitNetwork.swift
@@ -0,0 +1,156 @@
+//
+// ShareDownloadLimitNetwork.swift
+// Nextcloud
+//
+// Created by A118830248 on 11/11/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import SwiftyJSON
+import NextcloudKit
+import Alamofire
+
+class NMCCommunication: NSObject, XMLParserDelegate {
+
+ public static let shared: NMCCommunication = {
+ let instance = NMCCommunication()
+ return instance
+ }()
+
+ var message = ""
+ var foundCharacters = "";
+ var downloadLimit = DownloadLimit()
+ private lazy var appDelegate = UIApplication.shared.delegate as? AppDelegate
+ var controller: NCMainTabBarController!
+ var session: NCSession.Session {
+ NCSession.shared.getSession(controller: controller)
+ }
+
+ func getDownloadLimit(token: String, completion: @escaping (_ downloadLimit: DownloadLimit?, _ errorDescription: String) -> Void) {
+ let baseUrl = session.urlBase // NCBrandOptions.shared.loginBaseUrl
+
+ func getDownloadLimit(token: String, completion: @escaping (_ downloadLimit: DownloadLimit?, _ errorDescription: String) -> Void) {
+ let baseUrl = session.urlBase // NCBrandOptions.shared.loginBaseUrl
+ let endPoint = "/ocs/v2.php/apps/files_downloadlimit/\(token)/limit"
+ let path = baseUrl+endPoint
+ do {
+ var urlRequest = try URLRequest(url: URL(string: path)!, method: .get)
+ urlRequest.addValue("true", forHTTPHeaderField: "OCS-APIREQUEST")
+
+ let sessionConfiguration = URLSessionConfiguration.default
+ let urlSession = URLSession(configuration: sessionConfiguration)
+
+ let task = urlSession.dataTask(with: urlRequest) { [self] (data, response, error) in
+ guard error == nil else {
+ completion(nil, error?.localizedDescription ?? "")
+ return
+ }
+
+ if let httpResponse = response as? HTTPURLResponse {
+ let statusCode = httpResponse.statusCode
+ print("url: \(String(describing: httpResponse.url))\nStatus Code: \(statusCode)")
+ if httpResponse.statusCode == 200 {
+ let parser = XMLParser(data: data!)
+ parser.delegate = self
+ parser.parse()
+ completion(self.downloadLimit, self.message)
+ } else {
+ completion(nil, "Invalid Response code: \(statusCode)")
+ }
+ } else {
+ completion(nil, error?.localizedDescription ?? "Invalid Response")
+ }
+ }
+ task.resume()
+ } catch {
+ completion(nil, error.localizedDescription)
+ }
+ }
+
+ func setDownloadLimit(deleteLimit: Bool, limit: String, token: String, completion: @escaping (_ success: Bool, _ errorDescription: String) -> Void) {
+ let baseUrl = session.urlBase //NCBrandOptions.shared.loginBaseUrl
+ let baseUrl = appDelegate?.urlBase ?? "" //NCBrandOptions.shared.loginBaseUrl
+ let endPoint = "/ocs/v2.php/apps/files_downloadlimit/\(token)/limit"
+ let path = baseUrl+endPoint
+ do {
+
+ let method = deleteLimit ? HTTPMethod.delete : .put
+ var urlRequest = try URLRequest(url: URL(string: path)!, method: method)
+
+ urlRequest.addValue("true", forHTTPHeaderField: "OCS-APIREQUEST")
+ urlRequest.addValue(authorizationToken(), forHTTPHeaderField: "Authorization")
+ urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
+
+ let parameters = ["token": token, "limit": limit]
+
+ let encoder = JSONEncoder()
+ let jsonData = try encoder.encode(parameters)
+ urlRequest.httpBody = jsonData
+
+ let sessionConfiguration = URLSessionConfiguration.default
+ let urlSession = URLSession(configuration: sessionConfiguration)
+
+ let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in
+ guard error == nil else {
+ completion(false, error?.localizedDescription ?? "")
+ return
+ }
+
+ if let httpResponse = response as? HTTPURLResponse {
+ let statusCode = httpResponse.statusCode
+ print("url: \(String(describing: httpResponse.url))\nStatus Code: \(statusCode)")
+ if httpResponse.statusCode == 200 {
+ completion(true, error?.localizedDescription ?? "")
+ } else {
+ completion(false, "Invalid Response code: \(statusCode)")
+ }
+ } else {
+ completion(false, error?.localizedDescription ?? "Invalid Response")
+ }
+ }
+ task.resume()
+ } catch {
+ completion(false, error.localizedDescription)
+ }
+ }
+
+ public func authorizationToken() -> String {
+ let accountDetails = NCManageDatabase.shared.getAllTableAccount().first
+ let accountDetails = NCManageDatabase.shared.getAllAccount().first
+ let password = NCKeychain().getPassword(account: accountDetails?.account ?? "")
+ let password = NCKeychain().getPassword(account: accountDetails?.account ?? "")
+ let username = accountDetails?.user ?? ""
+ let credential = Data("\(username):\(password)".utf8).base64EncodedString()
+ return ("Basic \(credential)")
+ }
+
+
+ // MARK:- XML Parser Delegate
+ public func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
+
+ }
+ public func parser(_ parser: XMLParser, foundCharacters string: String) {
+ self.foundCharacters += string;
+ }
+
+ public func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
+ if elementName == "limit" {
+ let limit = self.foundCharacters.replacingOccurrences(of: "\n", with: "")
+ self.downloadLimit.limit = Int(limit.trimmingCharacters(in: .whitespaces))
+ }
+ if elementName == "count" {
+ let count = self.foundCharacters.replacingOccurrences(of: "\n", with: "")
+ self.downloadLimit.count = Int(count.trimmingCharacters(in: .whitespaces))
+ }
+ if elementName == "message"{
+ self.message = self.foundCharacters
+ }
+ self.foundCharacters = ""
+ }
+}
+
+//struct DownloadLimit: Codable {
+// var limit: Int?
+// var count: Int?
+//}