marinofaggiana 3 жил өмнө
parent
commit
7a00076a65
100 өөрчлөгдсөн 1859 нэмэгдсэн , 2429 устгасан
  1. 2 4
      .swiftlint.yml
  2. 1 1
      Cartfile.resolved
  3. 1 1
      File Provider Extension/FileProviderExtension.swift
  4. 1 1
      File Provider Extension/FileProviderItem.swift
  5. 175 368
      Nextcloud.xcodeproj/project.pbxproj
  6. 0 340
      Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  7. 1 2
      Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme
  8. 1 1
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme
  9. 1 2
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme
  10. 85 0
      NextcloudTests/ParallelWorkerTest.swift
  11. 4 4
      Share/NCShareExtension.swift
  12. 7 0
      iOSClient/.tx/config
  13. 1 1
      iOSClient/Activity/NCActivityTableViewCell.swift
  14. 0 1
      iOSClient/AppDelegate.swift
  15. 16 15
      iOSClient/Data/NCDataSource.swift
  16. 11 2
      iOSClient/Data/NCDatabase.swift
  17. 4 3
      iOSClient/Data/NCManageDatabase+Account.swift
  18. 1 1
      iOSClient/Data/NCManageDatabase.swift
  19. 2 0
      iOSClient/Data/NCManageDatabse+Metadata.swift
  20. 6 6
      iOSClient/EmptyView/NCEmptyView.xib
  21. 55 0
      iOSClient/Extensions/UIBarButton+Extension.swift
  22. 39 16
      iOSClient/Extensions/UIImage+Extensions.swift
  23. 7 5
      iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift
  24. 69 99
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  25. 3 2
      iOSClient/Main/Collection Common/NCGridCell.swift
  26. 130 0
      iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
  27. 3 0
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  28. 3 3
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
  29. 15 19
      iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard
  30. 227 259
      iOSClient/Main/NCFunctionCenter.swift
  31. 6 0
      iOSClient/Media/NCMedia.swift
  32. 16 272
      iOSClient/Menu/NCCollectionViewCommon+Menu.swift
  33. 16 101
      iOSClient/Menu/NCMedia+Menu.swift
  34. 0 30
      iOSClient/Menu/NCMenu.swift
  35. 215 0
      iOSClient/Menu/NCMenuAction.swift
  36. 53 70
      iOSClient/Menu/NCTrash+Menu.swift
  37. 11 115
      iOSClient/Menu/NCViewer+Menu.swift
  38. 5 4
      iOSClient/Menu/UIViewController+Menu.swift
  39. 1 1
      iOSClient/More/NCMore.swift
  40. 1 2
      iOSClient/NCGlobal.swift
  41. 8 5
      iOSClient/Networking/NCNetworking.swift
  42. 3 3
      iOSClient/Rename file/NCRenameFile.swift
  43. 226 0
      iOSClient/ScanDocument/NCScan+CollectionView.swift
  44. 20 17
      iOSClient/ScanDocument/NCScan.storyboard
  45. 372 0
      iOSClient/ScanDocument/NCScan.swift
  46. 18 2
      iOSClient/ScanDocument/NCScanCell.swift
  47. 0 642
      iOSClient/ScanDocument/ScanCollectionView.swift
  48. 1 1
      iOSClient/Security/NCEndToEndEncryption.m
  49. 2 2
      iOSClient/Select/NCSelect.swift
  50. 3 3
      iOSClient/Settings/CCManageAccount.m
  51. BIN
      iOSClient/Supporting Files/af.lproj/InfoPlist.strings
  52. BIN
      iOSClient/Supporting Files/af.lproj/Localizable.strings
  53. BIN
      iOSClient/Supporting Files/ar.lproj/InfoPlist.strings
  54. BIN
      iOSClient/Supporting Files/ar.lproj/Localizable.strings
  55. BIN
      iOSClient/Supporting Files/ast.lproj/InfoPlist.strings
  56. BIN
      iOSClient/Supporting Files/ast.lproj/Localizable.strings
  57. BIN
      iOSClient/Supporting Files/az.lproj/InfoPlist.strings
  58. BIN
      iOSClient/Supporting Files/az.lproj/Localizable.strings
  59. BIN
      iOSClient/Supporting Files/be.lproj/InfoPlist.strings
  60. BIN
      iOSClient/Supporting Files/be.lproj/Localizable.strings
  61. BIN
      iOSClient/Supporting Files/bg_BG.lproj/InfoPlist.strings
  62. BIN
      iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings
  63. BIN
      iOSClient/Supporting Files/bn_BD.lproj/InfoPlist.strings
  64. BIN
      iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings
  65. BIN
      iOSClient/Supporting Files/br.lproj/InfoPlist.strings
  66. BIN
      iOSClient/Supporting Files/br.lproj/Localizable.strings
  67. BIN
      iOSClient/Supporting Files/bs.lproj/InfoPlist.strings
  68. BIN
      iOSClient/Supporting Files/bs.lproj/Localizable.strings
  69. BIN
      iOSClient/Supporting Files/ca.lproj/InfoPlist.strings
  70. BIN
      iOSClient/Supporting Files/ca.lproj/Localizable.strings
  71. BIN
      iOSClient/Supporting Files/cs-CZ.lproj/InfoPlist.strings
  72. BIN
      iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings
  73. BIN
      iOSClient/Supporting Files/cy_GB.lproj/InfoPlist.strings
  74. BIN
      iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings
  75. BIN
      iOSClient/Supporting Files/da.lproj/InfoPlist.strings
  76. BIN
      iOSClient/Supporting Files/da.lproj/Localizable.strings
  77. BIN
      iOSClient/Supporting Files/de.lproj/InfoPlist.strings
  78. BIN
      iOSClient/Supporting Files/de.lproj/Localizable.strings
  79. BIN
      iOSClient/Supporting Files/el.lproj/InfoPlist.strings
  80. BIN
      iOSClient/Supporting Files/el.lproj/Localizable.strings
  81. BIN
      iOSClient/Supporting Files/en-GB.lproj/InfoPlist.strings
  82. BIN
      iOSClient/Supporting Files/en-GB.lproj/Localizable.strings
  83. 7 0
      iOSClient/Supporting Files/en.lproj/InfoPlist.strings
  84. 4 3
      iOSClient/Supporting Files/en.lproj/Localizable.strings
  85. BIN
      iOSClient/Supporting Files/eo.lproj/InfoPlist.strings
  86. BIN
      iOSClient/Supporting Files/eo.lproj/Localizable.strings
  87. BIN
      iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings
  88. BIN
      iOSClient/Supporting Files/es-419.lproj/Localizable.strings
  89. BIN
      iOSClient/Supporting Files/es-AR.lproj/InfoPlist.strings
  90. BIN
      iOSClient/Supporting Files/es-AR.lproj/Localizable.strings
  91. BIN
      iOSClient/Supporting Files/es-CL.lproj/InfoPlist.strings
  92. BIN
      iOSClient/Supporting Files/es-CL.lproj/Localizable.strings
  93. BIN
      iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings
  94. BIN
      iOSClient/Supporting Files/es-CO.lproj/Localizable.strings
  95. BIN
      iOSClient/Supporting Files/es-CR.lproj/InfoPlist.strings
  96. BIN
      iOSClient/Supporting Files/es-CR.lproj/Localizable.strings
  97. BIN
      iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings
  98. BIN
      iOSClient/Supporting Files/es-DO.lproj/Localizable.strings
  99. BIN
      iOSClient/Supporting Files/es-EC.lproj/InfoPlist.strings
  100. BIN
      iOSClient/Supporting Files/es-EC.lproj/Localizable.strings

+ 2 - 4
.swiftlint.yml

@@ -11,7 +11,8 @@ empty_count:
 
 line_length:
   # warning: 120
-  warning: 200
+  warning: 250
+  error: 250
 
 type_body_length:
   # error: 350
@@ -117,9 +118,6 @@ excluded:
   - iOSClient/Shares/NCShares.swift
   - iOSClient/Transfers/NCTransferCell.swift
   - iOSClient/Transfers/NCTransfers.swift
-  - iOSClient/Trash/Cell/NCTrashListCell.swift
-  - iOSClient/Trash/NCTrash.swift
-  - iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift
   - iOSClient/UserStatus/NCUserStatus.swift
   - iOSClient/Utility/NCAskAuthorization.swift
   - iOSClient/Utility/NCContentPresenter.swift

+ 1 - 1
Cartfile.resolved

@@ -1,3 +1,3 @@
 github "krzyzanowskim/OpenSSL" "1.1.1300"
 github "marinofaggiana/KTVHTTPCache" "2.0.2"
-github "marinofaggiana/TOPasscodeViewController" "7a750031bb86d9dc9f193bf34a38bbd288b3c4fd"
+github "marinofaggiana/TOPasscodeViewController" "a1b9d1058b2648e636525fc368e220a0cfddb42a"

+ 1 - 1
File Provider Extension/FileProviderExtension.swift

@@ -197,7 +197,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         }
 
         let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-        if tableLocalFile != nil && CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && tableLocalFile?.etag == metadata.etag {
+        if tableLocalFile != nil && CCUtility.fileProviderStorageExists(metadata) && tableLocalFile?.etag == metadata.etag {
             completionHandler(nil)
             return
         }

+ 1 - 1
File Provider Extension/FileProviderItem.swift

@@ -103,7 +103,7 @@ class FileProviderItem: NSObject, NSFileProviderItem {
         if metadata.directory {
             return true
         }
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+        if CCUtility.fileProviderStorageExists(metadata) {
             return true
         } else {
             return false

+ 175 - 368
Nextcloud.xcodeproj/project.pbxproj

@@ -27,6 +27,10 @@
 		AF22B218277D196700DAB0CC /* NCShareExtension+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */; };
 		AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */; };
 		AF2D7C7E2742559100ADF566 /* NCShareUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */; };
+		AF36077127BFA4E8001A243D /* ParallelWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077027BFA4E8001A243D /* ParallelWorker.swift */; };
+		AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077527BFB019001A243D /* ParallelWorkerTest.swift */; };
+		AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */; };
+		AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; };
 		AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
 		AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
 		AF4BF616275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
@@ -39,7 +43,10 @@
 		AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
 		AF4BF62027562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
 		AF4BF62127562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
+		AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF68326927BE65A90010BF0B /* NCMenuAction.swift */; };
 		AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */; };
+		AF7E504E27A2D8FF00B5E4AF /* UIBarButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */; };
+		AF7E505027A2D92300B5E4AF /* NCSelectableNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */; };
 		AF817EF1274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
 		AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
 		AF817EF3274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
@@ -125,6 +132,7 @@
 		F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7239870253D86B600257F49 /* NCEmptyDataSet.swift */; };
 		F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7239876253D86D300257F49 /* NCEmptyView.xib */; };
 		F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */; };
+		F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F72685E927C78E490019EF5E /* InfoPlist.strings */; };
 		F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */; };
 		F72928A0253B0937009CA4FD /* NCMainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F729289F253B0937009CA4FD /* NCMainNavigationController.swift */; };
 		F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72A47EB2487B06B005AD489 /* NCOperationQueue.swift */; };
@@ -184,9 +192,9 @@
 		F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */; };
 		F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */; };
 		F758A01227A7F03E0069468B /* JGProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F758A01127A7F03E0069468B /* JGProgressHUD */; };
-		F758B45A212C564000515F55 /* Scan.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F758B457212C564000515F55 /* Scan.storyboard */; };
-		F758B45E212C569D00515F55 /* ScanCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F758B45D212C569C00515F55 /* ScanCell.swift */; };
-		F758B460212C56A400515F55 /* ScanCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F758B45F212C56A400515F55 /* ScanCollectionView.swift */; };
+		F758B45A212C564000515F55 /* NCScan.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F758B457212C564000515F55 /* NCScan.storyboard */; };
+		F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F758B45D212C569C00515F55 /* NCScanCell.swift */; };
+		F758B460212C56A400515F55 /* NCScan.swift in Sources */ = {isa = PBXBuildFile; fileRef = F758B45F212C56A400515F55 /* NCScan.swift */; };
 		F75A9EE623796C6F0044CFCE /* NCNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75A9EE523796C6F0044CFCE /* NCNetworking.swift */; };
 		F75A9EE723796C6F0044CFCE /* NCNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75A9EE523796C6F0044CFCE /* NCNetworking.swift */; };
 		F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75AC2421F1F62450073EC19 /* NCManageAutoUploadFileName.swift */; };
@@ -319,6 +327,7 @@
 		F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginWeb.swift */; };
 		F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; };
 		F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; };
+		F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */; };
 		F7B7504B2397D38F004E13EC /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
 		F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F7B8B82F25681C3400967775 /* GoogleService-Info.plist */; };
 		F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
@@ -461,10 +470,16 @@
 		AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+Files.swift"; sourceTree = "<group>"; };
 		AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareLinkCell.swift; sourceTree = "<group>"; };
 		AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareUserCell.swift; sourceTree = "<group>"; };
+		AF36077027BFA4E8001A243D /* ParallelWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorker.swift; sourceTree = "<group>"; };
+		AF36077527BFB019001A243D /* ParallelWorkerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorkerTest.swift; sourceTree = "<group>"; };
+		AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCTrash+CollectionView.swift"; sourceTree = "<group>"; };
 		AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Account.swift"; sourceTree = "<group>"; };
 		AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabse+Metadata.swift"; sourceTree = "<group>"; };
 		AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Activity.swift"; sourceTree = "<group>"; };
+		AF68326927BE65A90010BF0B /* NCMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMenuAction.swift; sourceTree = "<group>"; };
 		AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+NCDelegate.swift"; sourceTree = "<group>"; };
+		AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBarButton+Extension.swift"; sourceTree = "<group>"; };
+		AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSelectableNavigationView.swift; sourceTree = "<group>"; };
 		AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUserBaseUrl.swift; sourceTree = "<group>"; };
 		AF8ED1F92757821000B8DBC4 /* NextcloudTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudTests.swift; sourceTree = "<group>"; };
@@ -582,6 +597,7 @@
 		F7239876253D86D300257F49 /* NCEmptyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCEmptyView.xib; sourceTree = "<group>"; };
 		F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareCommentsCell.xib; sourceTree = "<group>"; };
 		F7267A81225DFCE100D6DB7D /* AFNetworking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AFNetworking.framework; path = Carthage/Build/iOS/AFNetworking.framework; sourceTree = "<group>"; };
+		F72685E827C78E490019EF5E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCEndToEndInitialize.swift; sourceTree = "<group>"; };
 		F728B2BB23E83AD200E12DA0 /* Notification_Service_Extension.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Notification_Service_Extension.plist; sourceTree = "<group>"; };
 		F728B2BC23E83AD200E12DA0 /* Notification_Service_Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Notification_Service_Extension.entitlements; sourceTree = "<group>"; };
@@ -638,9 +654,9 @@
 		F755BD9A20594AC7008C5FBB /* NCService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCService.swift; sourceTree = "<group>"; };
 		F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCLoginWeb+Menu.swift"; sourceTree = "<group>"; };
 		F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCMedia+Menu.swift"; sourceTree = "<group>"; };
-		F758B457212C564000515F55 /* Scan.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Scan.storyboard; sourceTree = "<group>"; };
-		F758B45D212C569C00515F55 /* ScanCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanCell.swift; sourceTree = "<group>"; };
-		F758B45F212C56A400515F55 /* ScanCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanCollectionView.swift; sourceTree = "<group>"; };
+		F758B457212C564000515F55 /* NCScan.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCScan.storyboard; sourceTree = "<group>"; };
+		F758B45D212C569C00515F55 /* NCScanCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCScanCell.swift; sourceTree = "<group>"; };
+		F758B45F212C56A400515F55 /* NCScan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCScan.swift; sourceTree = "<group>"; };
 		F75A9EE523796C6F0044CFCE /* NCNetworking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCNetworking.swift; sourceTree = "<group>"; };
 		F75AC2421F1F62450073EC19 /* NCManageAutoUploadFileName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCManageAutoUploadFileName.swift; sourceTree = "<group>"; };
 		F75B0ABC244C4DBB00E58DCA /* NCFunctionCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCFunctionCenter.swift; sourceTree = "<group>"; };
@@ -755,6 +771,48 @@
 		F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; };
 		F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCFileViewInFolder.storyboard; sourceTree = "<group>"; };
 		F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCFileViewInFolder.swift; sourceTree = "<group>"; };
+		F7AA41B827C7CF4600494705 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41B927C7CF4B00494705 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41BA27C7CF5000494705 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41BB27C7CF5100494705 /* cs-CZ */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "cs-CZ"; path = "cs-CZ.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41BC27C7CF5300494705 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41BD27C7CF5400494705 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41BE27C7CF5600494705 /* ja-JP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja-JP"; path = "ja-JP.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41BF27C7CF5700494705 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C027C7CF5800494705 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41C127C7CF5900494705 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C227C7CF5A00494705 /* ka-GE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ka-GE"; path = "ka-GE.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41C327C7CF5B00494705 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C427C7CF5C00494705 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C527C7CF5D00494705 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C627C7CF5E00494705 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C727C7CF6000494705 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41C827C7CF6200494705 /* es-HN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-HN"; path = "es-HN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41C927C7CF6300494705 /* es-DO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-DO"; path = "es-DO.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41CA27C7CF6400494705 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41CB27C7CF6500494705 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41CC27C7CF6600494705 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41CD27C7CF6700494705 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41CE27C7CF6800494705 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41CF27C7CF6900494705 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41D027C7CF6900494705 /* sk-SK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sk-SK"; path = "sk-SK.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D127C7CF6A00494705 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41D227C7CF6C00494705 /* es-CO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CO"; path = "es-CO.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D327C7CF6D00494705 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D427C7CF6E00494705 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41D527C7CF6F00494705 /* es-CR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CR"; path = "es-CR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D627C7CF7100494705 /* es-GT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-GT"; path = "es-GT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D727C7CF7200494705 /* es-SV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-SV"; path = "es-SV.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D827C7CF7300494705 /* es-EC */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-EC"; path = "es-EC.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41D927C7CF7500494705 /* es-PR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PR"; path = "es-PR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41DA27C7CF7600494705 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		F7AA41DB27C7CF7800494705 /* es-UY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-UY"; path = "es-UY.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41DC27C7CF7900494705 /* es-PE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PE"; path = "es-PE.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41DD27C7CF7B00494705 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41DE27C7CF7D00494705 /* es-PA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PA"; path = "es-PA.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41DF27C7CF7E00494705 /* es-PY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PY"; path = "es-PY.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41E027C7CF8000494705 /* es-NI */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-NI"; path = "es-NI.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AA41E127C7CF8100494705 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		F7ACE4291BAC0268006C0017 /* Acknowledgements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Acknowledgements.h; sourceTree = "<group>"; };
 		F7ACE42A1BAC0268006C0017 /* Acknowledgements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Acknowledgements.m; sourceTree = "<group>"; };
 		F7ACE42B1BAC0268006C0017 /* Acknowledgements.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Acknowledgements.rtf; sourceTree = "<group>"; };
@@ -770,6 +828,7 @@
 		F7AF7632246BEDFE00B86E3C /* TOPasscodeViewController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TOPasscodeViewController.framework; path = Carthage/Build/iOS/TOPasscodeViewController.framework; sourceTree = "<group>"; };
 		F7B1076C25D3CF2800E72DE2 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; };
 		F7B1A7761EBB3C8000BFB6D1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+		F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCScan+CollectionView.swift"; sourceTree = "<group>"; };
 		F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
 		F7B8B82F25681C3400967775 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; };
 		F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingChunkedUpload.swift; sourceTree = "<group>"; };
@@ -937,6 +996,7 @@
 			children = (
 				3704EB2923D5A58400455C5B /* NCMenu.storyboard */,
 				371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */,
+				AF68326927BE65A90010BF0B /* NCMenuAction.swift */,
 				AF935066276B84E700BD078F /* NCMenu+FloatingPanel.swift */,
 				3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */,
 				8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */,
@@ -954,6 +1014,7 @@
 			isa = PBXGroup;
 			children = (
 				AF8ED2022757822700B8DBC4 /* NCGlobalTests.swift */,
+				AF36077527BFB019001A243D /* ParallelWorkerTest.swift */,
 				AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */,
 			);
 			path = NextcloudTests;
@@ -1118,6 +1179,7 @@
 			isa = PBXGroup;
 			children = (
 				F7E70DE91A24DE4100E1B66A /* Localizable.strings */,
+				F72685E927C78E490019EF5E /* InfoPlist.strings */,
 			);
 			name = Localizations;
 			sourceTree = "<group>";
@@ -1167,9 +1229,10 @@
 		F758B41E212C516300515F55 /* ScanDocument */ = {
 			isa = PBXGroup;
 			children = (
-				F758B457212C564000515F55 /* Scan.storyboard */,
-				F758B45D212C569C00515F55 /* ScanCell.swift */,
-				F758B45F212C56A400515F55 /* ScanCollectionView.swift */,
+				F758B457212C564000515F55 /* NCScan.storyboard */,
+				F758B45D212C569C00515F55 /* NCScanCell.swift */,
+				F758B45F212C56A400515F55 /* NCScan.swift */,
+				F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */,
 			);
 			path = ScanDocument;
 			sourceTree = "<group>";
@@ -1178,6 +1241,7 @@
 			isa = PBXGroup;
 			children = (
 				F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */,
+				AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */,
 				F78ACD3F21903CC20088454D /* NCGridCell.swift */,
 				F78ACD4521903D010088454D /* NCGridCell.xib */,
 				F78ACD4121903CE00088454D /* NCListCell.swift */,
@@ -1267,6 +1331,7 @@
 				F7632FC32183667400721B71 /* Section */,
 				F78F74332163757000C2ADAD /* NCTrash.storyboard */,
 				F78F74352163781100C2ADAD /* NCTrash.swift */,
+				AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */,
 			);
 			path = Trash;
 			sourceTree = "<group>";
@@ -1329,6 +1394,7 @@
 				F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */,
 				F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */,
 				F79B645F26CA661600838ACA /* UIControl+Extensions.swift */,
+				AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */,
 				F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */,
 				AFD3323F276A02C000F5AE02 /* UIApplication+Orientation.swift */,
 				F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */,
@@ -1439,6 +1505,7 @@
 				F70BFC7320E0FA7C00C67599 /* NCUtility.swift */,
 				AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */,
 				F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */,
+				AF36077027BFA4E8001A243D /* ParallelWorker.swift */,
 				F702F2FC25EE5D2C008F8E80 /* NYMnemonic */,
 			);
 			path = Utility;
@@ -1922,7 +1989,7 @@
 			isa = PBXProject;
 			attributes = {
 				LastSwiftUpdateCheck = 1310;
-				LastUpgradeCheck = 1250;
+				LastUpgradeCheck = 1320;
 				ORGANIZATIONNAME = "Marino Faggiana";
 				TargetAttributes = {
 					2C33C47E23E2C475005F963B = {
@@ -2105,13 +2172,14 @@
 				F77444F622281649000D5EB0 /* NCGridMediaCell.xib in Resources */,
 				F78ACD4421903CF20088454D /* NCListCell.xib in Resources */,
 				F78ACD4621903D010088454D /* NCGridCell.xib in Resources */,
+				F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */,
 				F769453C22E9CFFF000A798A /* NCShareUserCell.xib in Resources */,
 				F7A80BCA252624C100C7CD01 /* NCFileViewInFolder.storyboard in Resources */,
 				F76D3CF52428D0C1005DFA87 /* NCViewerPDF.storyboard in Resources */,
 				F700222C1EC479840080073F /* Custom.xcassets in Resources */,
 				F702F2F125EE5CDB008F8E80 /* NCLogin.storyboard in Resources */,
 				F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */,
-				F758B45A212C564000515F55 /* Scan.storyboard in Resources */,
+				F758B45A212C564000515F55 /* NCScan.storyboard in Resources */,
 				F70D87CF25EE6E58008CBBBD /* NCRenameFile.storyboard in Resources */,
 				F765F73225237E3F00391DBE /* NCRecent.storyboard in Resources */,
 				F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */,
@@ -2247,6 +2315,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */,
 				AF8ED1FC2757821000B8DBC4 /* NextcloudTests.swift in Sources */,
 				AF8ED2032757822700B8DBC4 /* NCGlobalTests.swift in Sources */,
 			);
@@ -2272,6 +2341,7 @@
 				AF4BF61A27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */,
 				AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */,
+				AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */,
 				AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */,
 				F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
@@ -2374,12 +2444,14 @@
 				AF4BF61927562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */,
 				F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */,
 				F716B75F26F09DF600D37EFC /* NCKTVHTTPCache.swift in Sources */,
+				AF36077127BFA4E8001A243D /* ParallelWorker.swift in Sources */,
 				F75A9EE623796C6F0044CFCE /* NCNetworking.swift in Sources */,
-				F758B460212C56A400515F55 /* ScanCollectionView.swift in Sources */,
+				F758B460212C56A400515F55 /* NCScan.swift in Sources */,
 				F78ACD52219046DC0088454D /* NCSectionHeaderFooter.swift in Sources */,
 				F749C10C23C4A5340027D966 /* NCIntroViewController.swift in Sources */,
 				F710D2022405826100A6033D /* NCViewer+Menu.swift in Sources */,
 				F77A697D250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift in Sources */,
+				AF7E504E27A2D8FF00B5E4AF /* UIBarButton+Extension.swift in Sources */,
 				F72928A0253B0937009CA4FD /* NCMainNavigationController.swift in Sources */,
 				F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */,
 				F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */,
@@ -2395,6 +2467,7 @@
 				F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */,
 				F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */,
+				AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */,
 				F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */,
 				F7A0D1352591FBC5008F8A13 /* String+Extensions.swift in Sources */,
 				F77B0E5F1D118A16002130FE /* NCSettings.m in Sources */,
@@ -2414,6 +2487,7 @@
 				3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */,
 				F73D5E47246DE09200DF6467 /* NCElementsJSON.swift in Sources */,
 				F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */,
+				F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */,
 				F7501C332212E57500FB1415 /* NCMedia.swift in Sources */,
 				F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */,
 				F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */,
@@ -2439,6 +2513,7 @@
 				F79B869B265E19D40085C0E0 /* NSMutableAttributedString+Extensions.swift in Sources */,
 				F7B7504B2397D38F004E13EC /* UIImage+Extensions.swift in Sources */,
 				F7EFC0CD256BF8DD00461AAD /* NCUserStatus.swift in Sources */,
+				AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */,
 				F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */,
 				F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */,
 				F7020FCE2233D7F700B7297D /* NCCreateFormUploadVoiceNote.swift in Sources */,
@@ -2466,7 +2541,7 @@
 				F70CEF5623E9C7E50007035B /* UIColor+Extensions.swift in Sources */,
 				F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */,
 				F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
-				F758B45E212C569D00515F55 /* ScanCell.swift in Sources */,
+				F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
 				F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */,
 				F77B0ED11D118A16002130FE /* Acknowledgements.m in Sources */,
 				F70D8D8124A4A9BF000A5756 /* NCNetworkingProcessUpload.swift in Sources */,
@@ -2474,6 +2549,7 @@
 				F7E4D9C422ED929B003675FD /* NCShareCommentsCell.swift in Sources */,
 				F717402E24F699A5000C87D5 /* NCFavorite.swift in Sources */,
 				AF2D7C7E2742559100ADF566 /* NCShareUserCell.swift in Sources */,
+				AF7E505027A2D92300B5E4AF /* NCSelectableNavigationView.swift in Sources */,
 				F74DE14325135B6800917068 /* NCTransfers.swift in Sources */,
 				AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				AF4BF61E27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
@@ -2506,6 +2582,57 @@
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
+		F72685E927C78E490019EF5E /* InfoPlist.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				F72685E827C78E490019EF5E /* en */,
+				F7AA41B827C7CF4600494705 /* ca */,
+				F7AA41B927C7CF4B00494705 /* zh-Hans */,
+				F7AA41BA27C7CF5000494705 /* zh-Hant-TW */,
+				F7AA41BB27C7CF5100494705 /* cs-CZ */,
+				F7AA41BC27C7CF5300494705 /* da */,
+				F7AA41BD27C7CF5400494705 /* nl */,
+				F7AA41BE27C7CF5600494705 /* ja-JP */,
+				F7AA41BF27C7CF5700494705 /* fr */,
+				F7AA41C027C7CF5800494705 /* en-GB */,
+				F7AA41C127C7CF5900494705 /* gl */,
+				F7AA41C227C7CF5A00494705 /* ka-GE */,
+				F7AA41C327C7CF5B00494705 /* de */,
+				F7AA41C427C7CF5C00494705 /* hu */,
+				F7AA41C527C7CF5D00494705 /* is */,
+				F7AA41C627C7CF5E00494705 /* it */,
+				F7AA41C727C7CF6000494705 /* tr */,
+				F7AA41C827C7CF6200494705 /* es-HN */,
+				F7AA41C927C7CF6300494705 /* es-DO */,
+				F7AA41CA27C7CF6400494705 /* ko */,
+				F7AA41CB27C7CF6500494705 /* nb-NO */,
+				F7AA41CC27C7CF6600494705 /* pl */,
+				F7AA41CD27C7CF6700494705 /* pt-BR */,
+				F7AA41CE27C7CF6800494705 /* pt-PT */,
+				F7AA41CF27C7CF6900494705 /* ru */,
+				F7AA41D027C7CF6900494705 /* sk-SK */,
+				F7AA41D127C7CF6A00494705 /* sr */,
+				F7AA41D227C7CF6C00494705 /* es-CO */,
+				F7AA41D327C7CF6D00494705 /* es-CL */,
+				F7AA41D427C7CF6E00494705 /* es */,
+				F7AA41D527C7CF6F00494705 /* es-CR */,
+				F7AA41D627C7CF7100494705 /* es-GT */,
+				F7AA41D727C7CF7200494705 /* es-SV */,
+				F7AA41D827C7CF7300494705 /* es-EC */,
+				F7AA41D927C7CF7500494705 /* es-PR */,
+				F7AA41DA27C7CF7600494705 /* sv */,
+				F7AA41DB27C7CF7800494705 /* es-UY */,
+				F7AA41DC27C7CF7900494705 /* es-PE */,
+				F7AA41DD27C7CF7B00494705 /* es-419 */,
+				F7AA41DE27C7CF7D00494705 /* es-PA */,
+				F7AA41DF27C7CF7E00494705 /* es-PY */,
+				F7AA41E027C7CF8000494705 /* es-NI */,
+				F7AA41E127C7CF8100494705 /* es-MX */,
+			);
+			name = InfoPlist.strings;
+			path = "Supporting Files";
+			sourceTree = "<group>";
+		};
 		F7E70DE91A24DE4100E1B66A /* Localizable.strings */ = {
 			isa = PBXVariantGroup;
 			children = (
@@ -2563,102 +2690,34 @@
 		2C33C48723E2C475005F963B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_NOTIFICATION_SERVICE,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE";
 				SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
 		};
 		2C33C48823E2C475005F963B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_NOTIFICATION_SERVICE,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				MTL_FAST_MATH = YES;
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE";
 				SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Release;
 		};
@@ -2666,38 +2725,11 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-				);
+				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
-				SWIFT_EMIT_LOC_STRINGS = NO;
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nextcloud.app/Nextcloud";
 			};
 			name = Debug;
@@ -2706,31 +2738,11 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
+				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_EMIT_LOC_STRINGS = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nextcloud.app/Nextcloud";
 			};
 			name = Release;
@@ -2738,301 +2750,107 @@
 		F7145A261D12E3B700CAFEEC /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_SHARE,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/Share.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE";
 				SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USE_HEADERMAP = YES;
 			};
 			name = Debug;
 		};
 		F7145A271D12E3B700CAFEEC /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_SHARE,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/Share.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE";
 				SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USE_HEADERMAP = YES;
 			};
 			name = Release;
 		};
 		F771E3F020E2392E00AFB62D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_FILE_PROVIDER_EXTENSION,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION";
 				SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
 		};
 		F771E3F120E2392E00AFB62D /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
 					EXTENSION_FILE_PROVIDER_EXTENSION,
 				);
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION";
 				SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Release;
 		};
 		F77B0F9B1D118A16002130FE /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
-				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = (
-					"-ObjC",
-					"-weak_framework",
-					SwiftUI,
-				);
-				OTHER_SWIFT_FLAGS = "";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE = "";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
 				SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USE_HEADERMAP = YES;
-				VERSIONING_SYSTEM = "";
 			};
 			name = Debug;
 		};
 		F77B0F9C1D118A16002130FE /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_STRICT_PROTOTYPES = NO;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				COPY_PHASE_STRIP = NO;
-				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
-				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
-				HEADER_SEARCH_PATHS = "";
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@executable_path/../../Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = "";
-				OTHER_LDFLAGS = (
-					"-ObjC",
-					"-weak_framework",
-					SwiftUI,
-				);
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE = "";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
 				SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h";
-				SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USE_HEADERMAP = YES;
-				VERSIONING_SYSTEM = "";
 			};
 			name = Release;
 		};
 		F7F67BC91A24D27800EE80DA /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
-				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
@@ -3040,66 +2858,58 @@
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 7;
-				DEFINES_MODULE = YES;
+				CURRENT_PROJECT_VERSION = 4;
+				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					DEBUG,
 					NC,
 				);
-				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNDECLARED_SELECTOR = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				MARKETING_VERSION = 4.2.2;
-				MTL_ENABLE_DEBUG_INFO = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				OTHER_LDFLAGS = (
-					"-Obj-C",
-					"-all_load",
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@executable_path/../../Frameworks",
 				);
+				MARKETING_VERSION = 4.3.0;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) NC";
-				SWIFT_SWIFT3_OBJC_INFERENCE = Off;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
 		};
 		F7F67BCA1A24D27800EE80DA /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
-				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
@@ -3107,45 +2917,42 @@
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 7;
-				DEFINES_MODULE = YES;
+				CURRENT_PROJECT_VERSION = 4;
+				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = NO;
-				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					NC,
 				);
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNDECLARED_SELECTOR = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 12.1;
-				MARKETING_VERSION = 4.2.2;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				OTHER_LDFLAGS = (
-					"-Obj-C",
-					"-all_load",
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@executable_path/../../Frameworks",
 				);
+				MARKETING_VERSION = 4.3.0;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG NC";
-				SWIFT_COMPILATION_MODE = wholemodule;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
-				SWIFT_SWIFT3_OBJC_INFERENCE = Off;
-				VALIDATE_PRODUCT = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Release;
 		};
@@ -3334,7 +3141,7 @@
 			repositoryURL = "https://github.com/nextcloud/ios-communication-library/";
 			requirement = {
 				kind = exactVersion;
-				version = 0.99.4;
+				version = 0.99.5;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {

+ 0 - 340
Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -1,340 +0,0 @@
-{
-  "object": {
-    "pins": [
-      {
-        "package": "abseil",
-        "repositoryURL": "https://github.com/firebase/abseil-cpp-SwiftPM.git",
-        "state": {
-          "branch": null,
-          "revision": "fffc3c2729be5747390ad02d5100291a0d9ad26a",
-          "version": "0.20200225.4"
-        }
-      },
-      {
-        "package": "Alamofire",
-        "repositoryURL": "https://github.com/Alamofire/Alamofire",
-        "state": {
-          "branch": null,
-          "revision": "f82c23a8a7ef8dc1a49a8bfc6a96883e79121864",
-          "version": "5.5.0"
-        }
-      },
-      {
-        "package": "BoringSSL-GRPC",
-        "repositoryURL": "https://github.com/firebase/boringssl-SwiftPM.git",
-        "state": {
-          "branch": null,
-          "revision": "734a8247442fde37df4364c21f6a0085b6a36728",
-          "version": "0.7.2"
-        }
-      },
-      {
-        "package": "ChromaColorPicker",
-        "repositoryURL": "https://github.com/marinofaggiana/ChromaColorPicker",
-        "state": {
-          "branch": "master",
-          "revision": "b1b2c58c3c5617c73863a073cb6393c79195932e",
-          "version": null
-        }
-      },
-      {
-        "package": "CocoaLumberjack",
-        "repositoryURL": "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
-        "state": {
-          "branch": null,
-          "revision": "80ada1f753b0d53d9b57c465936a7c4169375002",
-          "version": "3.7.4"
-        }
-      },
-      {
-        "package": "DropDown",
-        "repositoryURL": "https://github.com/AssistoLab/DropDown",
-        "state": {
-          "branch": "master",
-          "revision": "2ab6f6ce19f0117d1a76ea043ef8f57722c65d16",
-          "version": null
-        }
-      },
-      {
-        "package": "Firebase",
-        "repositoryURL": "https://github.com/firebase/firebase-ios-sdk",
-        "state": {
-          "branch": null,
-          "revision": "78f7087fd5d48eb7c36e299f330b6dddccd647b2",
-          "version": "8.12.1"
-        }
-      },
-      {
-        "package": "FloatingPanel",
-        "repositoryURL": "https://github.com/scenee/FloatingPanel",
-        "state": {
-          "branch": null,
-          "revision": "fe30e60235c5cc753e2ee2a3eebc8e654971634d",
-          "version": "2.5.2"
-        }
-      },
-      {
-        "package": "FSCalendar",
-        "repositoryURL": "https://github.com/WenchaoD/FSCalendar",
-        "state": {
-          "branch": null,
-          "revision": "afaf247581eb1f8aea847f2e6c99c665ae900494",
-          "version": "2.8.3"
-        }
-      },
-      {
-        "package": "GoogleAppMeasurement",
-        "repositoryURL": "https://github.com/google/GoogleAppMeasurement.git",
-        "state": {
-          "branch": null,
-          "revision": "6cc2991c11872510a5314bc112cc7558dd9d046a",
-          "version": "8.12.0"
-        }
-      },
-      {
-        "package": "GoogleDataTransport",
-        "repositoryURL": "https://github.com/google/GoogleDataTransport.git",
-        "state": {
-          "branch": null,
-          "revision": "15ccdfd25ac55b9239b82809531ff26605e7556e",
-          "version": "9.1.2"
-        }
-      },
-      {
-        "package": "GoogleUtilities",
-        "repositoryURL": "https://github.com/google/GoogleUtilities.git",
-        "state": {
-          "branch": null,
-          "revision": "b3bb0c5551fb3f80ca939829639ab5b093edd14f",
-          "version": "7.7.0"
-        }
-      },
-      {
-        "package": "gRPC",
-        "repositoryURL": "https://github.com/firebase/grpc-SwiftPM.git",
-        "state": {
-          "branch": null,
-          "revision": "fb405dd2c7901485f7e158b24e3a0a47e4efd8b5",
-          "version": "1.28.4"
-        }
-      },
-      {
-        "package": "GTMSessionFetcher",
-        "repositoryURL": "https://github.com/google/gtm-session-fetcher.git",
-        "state": {
-          "branch": null,
-          "revision": "bc6a19702ac76ac4e488b68148710eb815f9bc56",
-          "version": "1.7.0"
-        }
-      },
-      {
-        "package": "NCCommunication",
-        "repositoryURL": "https://github.com/nextcloud/ios-communication-library/",
-        "state": {
-          "branch": null,
-          "revision": "c7aabb42f734f30c2e325dec4000c59349f33005",
-          "version": "0.99.4"
-        }
-      },
-      {
-        "package": "JGProgressHUD",
-        "repositoryURL": "https://github.com/JonasGessner/JGProgressHUD.git",
-        "state": {
-          "branch": null,
-          "revision": "78d7cd35f1d90ff74fd82e486f2cbe4b24be8cf9",
-          "version": "2.2.0"
-        }
-      },
-      {
-        "package": "leveldb",
-        "repositoryURL": "https://github.com/firebase/leveldb.git",
-        "state": {
-          "branch": null,
-          "revision": "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
-          "version": "1.22.2"
-        }
-      },
-      {
-        "package": "MarkdownKit",
-        "repositoryURL": "https://github.com/bmoliveira/MarkdownKit",
-        "state": {
-          "branch": null,
-          "revision": "5056f3305d3499f44d8815530d560b87082e0cf5",
-          "version": "1.7.1"
-        }
-      },
-      {
-        "package": "MarqueeLabel",
-        "repositoryURL": "https://github.com/cbpowell/MarqueeLabel",
-        "state": {
-          "branch": null,
-          "revision": "f2c72a5f8568579dade6350dc26a482076d3d346",
-          "version": "4.3.0"
-        }
-      },
-      {
-        "package": "nanopb",
-        "repositoryURL": "https://github.com/firebase/nanopb.git",
-        "state": {
-          "branch": null,
-          "revision": "7ee9ef9f627d85cbe1b8c4f49a3ed26eed216c77",
-          "version": "2.30908.0"
-        }
-      },
-      {
-        "package": "Parchment",
-        "repositoryURL": "https://github.com/rechsteiner/Parchment",
-        "state": {
-          "branch": "main",
-          "revision": "cad6924f8a292eecaedc1bdefb57006f7979b9eb",
-          "version": null
-        }
-      },
-      {
-        "package": "Promises",
-        "repositoryURL": "https://github.com/google/promises.git",
-        "state": {
-          "branch": null,
-          "revision": "611337c330350c9c1823ad6d671e7f936af5ee13",
-          "version": "2.0.0"
-        }
-      },
-      {
-        "package": "QRCodeReader",
-        "repositoryURL": "https://github.com/yannickl/QRCodeReader.swift",
-        "state": {
-          "branch": null,
-          "revision": "5020b5a47199d8ba80c83a4b4fafd70e9dc9dc7f",
-          "version": "10.1.1"
-        }
-      },
-      {
-        "package": "Queuer",
-        "repositoryURL": "https://github.com/FabrizioBrancati/Queuer",
-        "state": {
-          "branch": null,
-          "revision": "52515108d0ac4616d9e15ffcc7ad986e300d31ff",
-          "version": "2.1.1"
-        }
-      },
-      {
-        "package": "QuickLayout",
-        "repositoryURL": "https://github.com/huri000/QuickLayout",
-        "state": {
-          "branch": null,
-          "revision": "6be62decbe508d8fc8f9dbafc349d05bab03c38b",
-          "version": "3.0.1"
-        }
-      },
-      {
-        "package": "RealmDatabase",
-        "repositoryURL": "https://github.com/realm/realm-core",
-        "state": {
-          "branch": null,
-          "revision": "6b81f1a7a2d421f9e0b9e7f04e76bcf736a54409",
-          "version": "11.9.0"
-        }
-      },
-      {
-        "package": "Realm",
-        "repositoryURL": "https://github.com/realm/realm-swift",
-        "state": {
-          "branch": null,
-          "revision": "9dff9f2862240d521ad6ad599541269177ddb993",
-          "version": "10.22.0"
-        }
-      },
-      {
-        "package": "SVGKit",
-        "repositoryURL": "https://github.com/SVGKit/SVGKit.git",
-        "state": {
-          "branch": "3.x",
-          "revision": "fbd7fc73c4ee5593fde57b9d50fe53d307dcf07c",
-          "version": null
-        }
-      },
-      {
-        "package": "swift-log",
-        "repositoryURL": "https://github.com/apple/swift-log.git",
-        "state": {
-          "branch": null,
-          "revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7",
-          "version": "1.4.2"
-        }
-      },
-      {
-        "package": "SwiftProtobuf",
-        "repositoryURL": "https://github.com/apple/swift-protobuf.git",
-        "state": {
-          "branch": null,
-          "revision": "7e2c5f3cbbeea68e004915e3a8961e20bd11d824",
-          "version": "1.18.0"
-        }
-      },
-      {
-        "package": "SwiftEntryKit",
-        "repositoryURL": "https://github.com/huri000/SwiftEntryKit",
-        "state": {
-          "branch": null,
-          "revision": "c2d42574e4fe4e1f9719843f35add7922942a16b",
-          "version": "1.2.7"
-        }
-      },
-      {
-        "package": "SwiftRichString",
-        "repositoryURL": "https://github.com/malcommac/SwiftRichString",
-        "state": {
-          "branch": null,
-          "revision": "9bf4b5af6bb4386865636fc504d6c588c2b65040",
-          "version": "3.7.2"
-        }
-      },
-      {
-        "package": "SwiftyJSON",
-        "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON",
-        "state": {
-          "branch": null,
-          "revision": "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07",
-          "version": "5.0.1"
-        }
-      },
-      {
-        "package": "SwiftyXMLParser",
-        "repositoryURL": "https://github.com/yahoojapan/SwiftyXMLParser",
-        "state": {
-          "branch": null,
-          "revision": "d7a1d23f04c86c1cd2e8f19247dd15d74e0ea8be",
-          "version": "5.6.0"
-        }
-      },
-      {
-        "package": "TLPhotoPicker",
-        "repositoryURL": "https://github.com/tilltue/TLPhotoPicker",
-        "state": {
-          "branch": null,
-          "revision": "0d0cbbd2d20ed5fd36e5f4052209f5e2d9aaa8b7",
-          "version": "2.1.9"
-        }
-      },
-      {
-        "package": "UICKeyChainStore",
-        "repositoryURL": "https://github.com/kishikawakatsumi/UICKeyChainStore",
-        "state": {
-          "branch": "master",
-          "revision": "db869212bc69b6198a62efe03e2f5fc8e19c6b65",
-          "version": null
-        }
-      },
-      {
-        "package": "XLForm",
-        "repositoryURL": "https://github.com/xmartlabs/XLForm",
-        "state": {
-          "branch": null,
-          "revision": "870afc56602fd518e33d0b271371a2d5acd410ea",
-          "version": "4.3.0"
-        }
-      }
-    ]
-  },
-  "version": 1
-}

+ 1 - 2
Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1250"
+   LastUpgradeVersion = "1320"
    wasCreatedForAppExtension = "YES"
    version = "2.0">
    <BuildAction
@@ -93,7 +93,6 @@
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
       debugDocumentVersioning = "YES"
-      askForAppToLaunch = "Yes"
       launchAutomaticallySubstyle = "2">
       <BuildableProductRunnable
          runnableDebuggingMode = "0">

+ 1 - 1
Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1250"
+   LastUpgradeVersion = "1320"
    version = "1.7">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 2
Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1250"
+   LastUpgradeVersion = "1320"
    wasCreatedForAppExtension = "YES"
    version = "2.0">
    <BuildAction
@@ -97,7 +97,6 @@
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
       debugDocumentVersioning = "YES"
-      askForAppToLaunch = "Yes"
       launchAutomaticallySubstyle = "2">
       <BuildableProductRunnable
          runnableDebuggingMode = "0">

+ 85 - 0
NextcloudTests/ParallelWorkerTest.swift

@@ -0,0 +1,85 @@
+//
+//  ParallelWorkerTest.swift
+//  Nextcloud
+//
+//  Created by Henrik Storch on 18.02.22.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+@testable import Nextcloud
+import XCTest
+
+class ParallelWorkerTest: XCTestCase {
+
+    func testWorkerComplete() throws {
+        let expectation = XCTestExpectation(description: "Worker executes all tasks")
+        let taskCount = 20
+        var tasksComplete = 0
+        let worker = ParallelWorker(n: 5, titleKey: nil, totalTasks: nil, hudView: nil)
+        for _ in 0..<taskCount {
+            worker.execute { completion in
+                tasksComplete += 1
+                completion()
+            }
+        }
+        worker.completeWork {
+            XCTAssertEqual(tasksComplete, taskCount)
+            if tasksComplete == taskCount {
+                expectation.fulfill()
+            }
+        }
+
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+
+    func testWorkerOrder() throws {
+        let expectation = XCTestExpectation(description: "Worker executes work in sequence for n = 1")
+        let sortedArray = Array(0..<20)
+        var array: [Int] = []
+        let worker = ParallelWorker(n: 1, titleKey: nil, totalTasks: nil, hudView: nil)
+        for i in sortedArray {
+            worker.execute { completion in
+                DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0...0.2)) {
+                    array.append(i)
+                    completion()
+                }
+            }
+        }
+        worker.completeWork {
+            XCTAssertEqual(sortedArray, array)
+            if sortedArray == array {
+                expectation.fulfill()
+            }
+        }
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+
+    func testWorkerFailsWithoutCompletion() throws {
+        let expectation = XCTestExpectation(description: "Worker fails if completion isn't called")
+        expectation.isInverted = true
+        let worker = ParallelWorker(n: 5, titleKey: nil, totalTasks: nil, hudView: nil)
+        for _ in 0..<20 {
+            worker.execute { _ in }
+        }
+        worker.completeWork { expectation.fulfill() }
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(result, .completed)
+    }
+}

+ 4 - 4
Share/NCShareExtension.swift

@@ -104,7 +104,7 @@ class NCShareExtension: UIViewController {
         createFolderView.addGestureRecognizer(createFolderGesture)
 
         uploadView.layer.cornerRadius = 10
-        
+
         // uploadImage.image = NCUtility.shared.loadImage(named: "square.and.arrow.up", color: NCBrandColor.shared.label)
         uploadLabel.text = NSLocalizedString("_upload_", comment: "")
         uploadLabel.textColor = .systemBlue
@@ -130,7 +130,7 @@ class NCShareExtension: UIViewController {
         if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
             indicatorView.ringWidth = 1.5
         }
-        
+
         NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil)
     }
 
@@ -188,7 +188,7 @@ class NCShareExtension: UIViewController {
         guard let progress = notification.userInfo?["progress"] as? Float else { return }
         hud.progress = progress
     }
-    
+
     func setNavigationBar(navigationTitle: String) {
 
         navigationItem.title = navigationTitle
@@ -357,7 +357,7 @@ extension NCShareExtension {
         hud.textLabel.text = NSLocalizedString("_upload_file_", comment: "") + " \(counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(filesName.count)"
         hud.progress = 0
         hud.show(in: self.view)
-        
+
         NCNetworking.shared.upload(metadata: metadata) { } completion: { errorCode, _ in
             if errorCode == 0 {
                 self.counterUploaded += 1

+ 7 - 0
iOSClient/.tx/config

@@ -7,3 +7,10 @@ source_file = Supporting Files/en.lproj/Localizable.strings
 source_lang = en
 type = STRINGS
 lang_map = pt_BR:pt-BR,zh_CN:zh-Hans,fi_FI:fi-FI,es_MX:es-MX,nb_NO:nb-NO,cs_CZ:cs-CZ,en_GB:en-GB,es_AR:es-AR,sk_SK:sk-SK,hu_HU:hu,ka_GE:ka-GE,zh_TW:zh-Hant-TW,es_CL:es-CL,es_CO:es-CO,es_CR:es-CR,es_DO:es-DO,es_EC:es-EC,es_GT:es-GT,es_HN:es-HN,es_NI:es-NI,es_PA:es-PA,es_PE:es-PE,es_PR:es-PR,es_PY:es-PY,es_SV:es-SV,es_UY:es-UY,es_419:es-419,pt_PT:pt-PT,ja_JP:ja-JP
+
+[nextcloud.ios-info]
+file_filter = Supporting Files/<lang>.lproj/InfoPlist.strings
+source_file = Supporting Files/en.lproj/InfoPlist.strings
+source_lang = en
+type = STRINGS
+lang_map = pt_BR:pt-BR,zh_CN:zh-Hans,fi_FI:fi-FI,es_MX:es-MX,nb_NO:nb-NO,cs_CZ:cs-CZ,en_GB:en-GB,es_AR:es-AR,sk_SK:sk-SK,hu_HU:hu,ka_GE:ka-GE,zh_TW:zh-Hant-TW,es_CL:es-CL,es_CO:es-CO,es_CR:es-CR,es_DO:es-DO,es_EC:es-EC,es_GT:es-GT,es_HN:es-HN,es_NI:es-NI,es_PA:es-PA,es_PE:es-PE,es_PR:es-PR,es_PY:es-PY,es_SV:es-SV,es_UY:es-UY,es_419:es-419,pt_PT:pt-PT,ja_JP:ja-JP

+ 1 - 1
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -111,7 +111,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             var responder: UIResponder? = collectionView
             while !(responder is UIViewController) {
                 responder = responder?.next
-                if nil == responder {
+                if responder == nil {
                     break
                 }
             }

+ 0 - 1
iOSClient/AppDelegate.swift

@@ -59,7 +59,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var disableSharesView: Bool = false
     var documentPickerViewController: NCDocumentPickerViewController?
     var networkingProcessUpload: NCNetworkingProcessUpload?
-    var pasteboardOcIds: [String] = []
     var shares: [tableShare] = []
     var timerErrorNetworking: Timer?
     

+ 16 - 15
iOSClient/Data/NCDataSource.swift

@@ -114,16 +114,13 @@ class NCDataSource: NSObject {
             }
 
             // is Local / offline
-            if !metadata.directory {
-                let size = CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView)
-                if size > 0 {
-                    let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                    if tableLocalFile == nil && size == metadata.size {
-                        NCManageDatabase.shared.addLocalFile(metadata: metadata)
-                    }
-                    if tableLocalFile?.offline ?? false {
-                        metadataOffLine.append(metadata.ocId)
-                    }
+            if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) {
+                let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                if tableLocalFile == nil {
+                    NCManageDatabase.shared.addLocalFile(metadata: metadata)
+                }
+                if tableLocalFile?.offline ?? false {
+                    metadataOffLine.append(metadata.ocId)
                 }
             }
 
@@ -183,15 +180,19 @@ class NCDataSource: NSObject {
 
         var index: Int?
 
-        if ocIdTemp != nil {
-            index = self.getIndexMetadata(ocId: ocIdTemp!)
+        if let ocIdTemp = ocIdTemp {
+            index = self.getIndexMetadata(ocId: ocIdTemp)
         } else {
             index = self.getIndexMetadata(ocId: ocId)
         }
 
-        if index != nil {
-            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                metadatas[index!] = metadata
+        guard let index = index, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil }
+        metadatas[index] = metadata
+
+        if CCUtility.fileProviderStorageExists(metadata) {
+            let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            if tableLocalFile?.offline ?? false {
+                metadataOffLine.append(metadata.ocId)
             }
         }
 

+ 11 - 2
iOSClient/Data/NCDatabase.swift

@@ -24,6 +24,7 @@
 
 import UIKit
 import RealmSwift
+import NCCommunication
 
 protocol DateCompareable {
     var dateKey: Date { get }
@@ -52,7 +53,6 @@ class tableAccount: Object, NCUserBaseUrl {
     @objc dynamic var businessSize: String = ""
     @objc dynamic var businessType = ""
     @objc dynamic var city = ""
-    @objc dynamic var company = ""
     @objc dynamic var country = ""
     @objc dynamic var displayName = ""
     @objc dynamic var email = ""
@@ -62,6 +62,7 @@ class tableAccount: Object, NCUserBaseUrl {
     @objc dynamic var lastLogin: Int64 = 0
     @objc dynamic var locale = ""
     @objc dynamic var mediaPath = ""
+    @objc dynamic var organisation = ""
     @objc dynamic var password = ""
     @objc dynamic var phone = ""
     @objc dynamic var quota: Int64 = 0
@@ -83,7 +84,7 @@ class tableAccount: Object, NCUserBaseUrl {
     @objc dynamic var userStatusMessageIsPredefined: Bool = false
     @objc dynamic var userStatusStatus: String?
     @objc dynamic var userStatusStatusIsUserDefined: Bool = false
-    @objc dynamic var webpage = ""
+    @objc dynamic var website = ""
     @objc dynamic var zip = ""
 
     // COLOR Files
@@ -413,6 +414,14 @@ class tableMetadata: Object, NCUserBaseUrl {
     }
 }
 
+extension tableMetadata {
+    var fileExtension: String { (fileNameView as NSString).pathExtension }
+
+    var isPrintable: Bool {
+        classFile == NCCommunicationCommon.typeClassFile.image.rawValue || ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/")
+    }
+}
+
 class tablePhotoLibrary: Object {
 
     @objc dynamic var account = ""

+ 4 - 3
iOSClient/Data/NCManageDatabase+Account.swift

@@ -302,6 +302,7 @@ extension NCManageDatabase {
                 result.language = userProfile.language
                 result.lastLogin = userProfile.lastLogin
                 result.locale = userProfile.locale
+                result.organisation = userProfile.organisation
                 result.phone = userProfile.phone
                 result.quota = userProfile.quota
                 result.quotaFree = userProfile.quotaFree
@@ -312,7 +313,7 @@ extension NCManageDatabase {
                 result.subadmin = userProfile.subadmin.joined(separator: ",")
                 result.twitter = userProfile.twitter
                 result.userId = userProfile.userId
-                result.webpage = userProfile.webpage
+                result.website = userProfile.website
 
                 returnAccount = result
             }
@@ -323,7 +324,7 @@ extension NCManageDatabase {
         return tableAccount.init(value: returnAccount)
     }
 
-    @objc func setAccountUserProfileHC(businessSize: String, businessType: String, city: String, company: String, country: String, role: String, zip: String) -> tableAccount? {
+    @objc func setAccountUserProfileHC(businessSize: String, businessType: String, city: String, organisation: String, country: String, role: String, zip: String) -> tableAccount? {
 
         let realm = try! Realm()
 
@@ -343,7 +344,7 @@ extension NCManageDatabase {
                 result.businessSize = businessSize
                 result.businessType = businessType
                 result.city = city
-                result.company = company
+                result.organisation =  organisation
                 result.country = country
                 result.role = role
                 result.zip = zip

+ 1 - 1
iOSClient/Data/NCManageDatabase.swift

@@ -1131,7 +1131,7 @@ class NCManageDatabase: NSObject {
         do {
             try realm.safeWrite {
 
-                let addObject = tableLocalFile()
+                let addObject = getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) ?? tableLocalFile()
 
                 addObject.account = metadata.account
                 addObject.etag = metadata.etag

+ 2 - 0
iOSClient/Data/NCManageDatabse+Metadata.swift

@@ -141,6 +141,8 @@ extension NCManageDatabase {
 
         let metadata = tableMetadata()
         let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: contentType, directory: false)
+        
+        let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
 
         metadata.account = account
         metadata.chunk = false

+ 6 - 6
iOSClient/EmptyView/NCEmptyView.xib

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina3_5" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -23,12 +23,12 @@
                     </constraints>
                 </imageView>
                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="crs-DO-owR">
-                    <rect key="frame" x="0.0" y="180" width="350" height="24"/>
+                    <rect key="frame" x="20" y="180" width="310" height="24"/>
                     <fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
                     <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                     <nil key="highlightedColor"/>
                 </label>
-                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D4p-sI-mNB">
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D4p-sI-mNB">
                     <rect key="frame" x="20" y="224" width="310" height="17"/>
                     <constraints>
                         <constraint firstAttribute="height" relation="lessThanOrEqual" constant="50" id="u7B-jW-bWI"/>
@@ -41,10 +41,10 @@
             <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
             <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
             <constraints>
-                <constraint firstItem="crs-DO-owR" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="CMU-Tp-bUM"/>
+                <constraint firstItem="crs-DO-owR" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="CMU-Tp-bUM"/>
                 <constraint firstItem="W3d-Us-kU4" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="Fyb-so-iAw"/>
                 <constraint firstItem="D4p-sI-mNB" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="egV-G4-wax"/>
-                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="crs-DO-owR" secondAttribute="trailing" id="hHl-iN-Gev"/>
+                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="crs-DO-owR" secondAttribute="trailing" constant="20" id="hHl-iN-Gev"/>
                 <constraint firstItem="crs-DO-owR" firstAttribute="top" secondItem="W3d-Us-kU4" secondAttribute="bottom" constant="30" id="hLN-L6-0gH"/>
                 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="D4p-sI-mNB" secondAttribute="trailing" constant="20" id="imv-AK-mqu"/>
                 <constraint firstItem="W3d-Us-kU4" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="kma-1Q-c3Q"/>

+ 55 - 0
iOSClient/Extensions/UIBarButton+Extension.swift

@@ -0,0 +1,55 @@
+//
+//  UIBarButton+Extension.swift
+//  Nextcloud
+//
+//  Created by Henrik Storch on 27.01.22.
+//  Copyright © 2022 Henrik Storch. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+
+private var actionKey: Void?
+
+extension UIBarButtonItem {
+    // https://stackoverflow.com/a/36983811/9506784
+    private var _action: () -> Void {
+        get {
+            return objc_getAssociatedObject(self, &actionKey) as? () -> Void ?? { }
+        }
+        set {
+            objc_setAssociatedObject(self, &actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+
+    convenience init(title: String?, style: UIBarButtonItem.Style, action: @escaping () -> Void) {
+        self.init(title: title, style: style, target: nil, action: #selector(pressed))
+        self.target = self
+        self._action = action
+    }
+
+    convenience init(image: UIImage?, style: UIBarButtonItem.Style, action: @escaping () -> Void) {
+        self.init(image: image, style: style, target: nil, action: #selector(pressed))
+        self.target = self
+        self._action = action
+    }
+
+    @objc private func pressed(sender: UIBarButtonItem) {
+        _action()
+    }
+}

+ 39 - 16
iOSClient/Extensions/UIImage+Extensions.swift

@@ -117,27 +117,25 @@ extension UIImage {
 
     @objc func image(color: UIColor, size: CGFloat) -> UIImage {
 
-        return autoreleasepool { () -> UIImage in
-            let size = CGSize(width: size, height: size)
+        let size = CGSize(width: size, height: size)
 
-            UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
-            color.setFill()
+        UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
+        color.setFill()
 
-            let context = UIGraphicsGetCurrentContext()
-            context?.translateBy(x: 0, y: size.height)
-            context?.scaleBy(x: 1.0, y: -1.0)
-            context?.setBlendMode(CGBlendMode.normal)
+        let context = UIGraphicsGetCurrentContext()
+        context?.translateBy(x: 0, y: size.height)
+        context?.scaleBy(x: 1.0, y: -1.0)
+        context?.setBlendMode(CGBlendMode.normal)
 
-            let rect = CGRect(origin: .zero, size: size)
-            guard let cgImage = self.cgImage else { return self }
-            context?.clip(to: rect, mask: cgImage)
-            context?.fill(rect)
+        let rect = CGRect(origin: .zero, size: size)
+        guard let cgImage = self.cgImage else { return self }
+        context?.clip(to: rect, mask: cgImage)
+        context?.fill(rect)
 
-            let newImage = UIGraphicsGetImageFromCurrentImageContext() ?? self
-            UIGraphicsEndImageContext()
+        let newImage = UIGraphicsGetImageFromCurrentImageContext() ?? self
+        UIGraphicsEndImageContext()
 
-            return newImage
-        }
+        return newImage        
     }
 
     func imageColor(_ color: UIColor) -> UIImage {
@@ -204,4 +202,29 @@ extension UIImage {
         // Return the downsampled image as UIImage
         return UIImage(cgImage: downsampledImage)
     }
+    
+    // Source:
+    // https://stackoverflow.com/questions/27092354/rotating-uiimage-in-swift/47402811#47402811
+    
+    func rotate(radians: Float) -> UIImage? {
+        var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
+        // Trim off the extremely small float value to prevent core graphics from rounding it up
+        newSize.width = floor(newSize.width)
+        newSize.height = floor(newSize.height)
+
+        UIGraphicsBeginImageContextWithOptions(newSize, true, self.scale)
+        let context = UIGraphicsGetCurrentContext()!
+
+        // Move origin to middle
+        context.translateBy(x: newSize.width / 2, y: newSize.height / 2)
+        // Rotate around middle
+        context.rotate(by: CGFloat(radians))
+        // Draw the image at its center
+        self.draw(in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
+
+        let newImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+
+        return newImage
+    }
 }

+ 7 - 5
iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift

@@ -37,7 +37,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
     open weak var delegate: NCAudioRecorderViewControllerDelegate?
     var recording: NCAudioRecorder!
-    var recordDuration: TimeInterval = 0
+    var startDate: Date = Date()
     var fileName: String = ""
 
     @IBOutlet weak var contentContainerView: UIView!
@@ -93,7 +93,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
         if recording.state == .record {
 
-            recordDuration = 0
+            startDate = Date()
             recording.stop()
             voiceRecordHUD.update(0.0)
 
@@ -103,7 +103,6 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
         } else {
 
-            recordDuration = 0
             do {
                 try recording.record()
                 startStopLabel.text = NSLocalizedString("_voice_memo_stop_", comment: "")
@@ -149,8 +148,11 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
         voiceRecordHUD.update(CGFloat(rate))
         voiceRecordHUD.fillColor = UIColor.green
-        recordDuration += 1
-        durationLabel.text =  String().formatSecondsToString(recordDuration/60)
+        
+        let formatter = DateComponentsFormatter()
+        formatter.allowedUnits = [.second]
+        formatter.unitsStyle = .full
+        durationLabel.text = formatter.string(from: startDate, to: Date())
     }
 }
 

+ 69 - 99
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -22,9 +22,11 @@
 //
 
 import UIKit
+import Realm
 import NCCommunication
 
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate, NCSelectableNavigationView {
+    var selectableDataSource: [RealmSwiftObject] { dataSource.metadatas }
 
     @IBOutlet weak var collectionView: UICollectionView!
 
@@ -448,13 +450,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func downloadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let _ = userInfo["errorCode"] as? Int, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if let row = dataSource.reloadMetadata(ocId: metadata.ocId) {
-                let indexPath = IndexPath(row: row, section: 0)
-                if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
-                    collectionView?.reloadItems(at: [indexPath])
-                }
-            }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let _ = userInfo["errorCode"] as? Int,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              let row = dataSource.reloadMetadata(ocId: metadata.ocId)
+        else { return }
+        let indexPath = IndexPath(row: row, section: 0)
+        if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
+            collectionView?.reloadItems(at: [indexPath])
         }
     }
 
@@ -577,76 +581,63 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     // MARK: - Layout
 
     @objc func setNavigationItem() {
-
-        if isEditMode {
-
-            navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "navigationMore"), style: .plain, target: self, action: #selector(tapSelectMenu(sender:)))
-            navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .plain, target: self, action: #selector(tapSelect(sender:)))
-            navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(dataSource.metadatas.count)"
-
-        } else {
-
-            navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(tapSelect(sender:)))
-            navigationItem.leftBarButtonItem = nil
-            navigationItem.title = titleCurrentFolder
-
-            // PROFILE BUTTON
-
-            if layoutKey == NCGlobal.shared.layoutViewFiles {
-                let activeAccount = NCManageDatabase.shared.getActiveAccount()
-
-                let image = NCUtility.shared.loadUserImage(
-                    for: appDelegate.user,
-                       displayName: activeAccount?.displayName,
-                       userBaseUrl: appDelegate)
-
-                let button = UIButton(type: .custom)
-                button.setImage(image, for: .normal)
-
-                if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) {
-
-                    var titleButton = "  "
-
-                    if getNavigationTitle() == activeAccount?.alias {
-                        titleButton = ""
-                    } else {
-                        titleButton += activeAccount?.displayName ?? ""
-                    }
-
-                    button.setTitle(titleButton, for: .normal)
-                    button.setTitleColor(.systemBlue, for: .normal)
-                }
-
-                button.semanticContentAttribute = .forceLeftToRight
-                button.sizeToFit()
-                button.action(for: .touchUpInside) { _ in
-
-                    let accounts = NCManageDatabase.shared.getAllAccountOrderAlias()
-                    if accounts.count > 0 {
-
-                        if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
-
-                            vcAccountRequest.activeAccount = NCManageDatabase.shared.getActiveAccount()
-                            vcAccountRequest.accounts = accounts
-                            vcAccountRequest.enableTimerProgress = false
-                            vcAccountRequest.enableAddAccount = true
-                            vcAccountRequest.delegate = self
-                            vcAccountRequest.dismissDidEnterBackground = true
-
-                            let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
-                            let numberCell = accounts.count + 1
-                            let height = min(CGFloat(numberCell * Int(vcAccountRequest.heightCell) + 45), screenHeighMax)
-
-                            let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height)
-
-                            UIApplication.shared.keyWindow?.rootViewController?.present(popup, animated: true)
-                        }
-                    }
+        self.setNavigationHeader()
+        guard !isEditMode, layoutKey == NCGlobal.shared.layoutViewFiles else { return }
+        
+        // PROFILE BUTTON
+        
+        let activeAccount = NCManageDatabase.shared.getActiveAccount()
+        
+        let image = NCUtility.shared.loadUserImage(
+            for: appDelegate.user,
+               displayName: activeAccount?.displayName,
+               userBaseUrl: appDelegate)
+        
+        let button = UIButton(type: .custom)
+        button.setImage(image, for: .normal)
+        
+        if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) {
+            
+            var titleButton = "  "
+            
+            if getNavigationTitle() == activeAccount?.alias {
+                titleButton = ""
+            } else {
+                titleButton += activeAccount?.displayName ?? ""
+            }
+            
+            button.setTitle(titleButton, for: .normal)
+            button.setTitleColor(.systemBlue, for: .normal)
+        }
+        
+        button.semanticContentAttribute = .forceLeftToRight
+        button.sizeToFit()
+        button.action(for: .touchUpInside) { _ in
+            
+            let accounts = NCManageDatabase.shared.getAllAccountOrderAlias()
+            if accounts.count > 0 {
+                
+                if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
+                    
+                    vcAccountRequest.activeAccount = NCManageDatabase.shared.getActiveAccount()
+                    vcAccountRequest.accounts = accounts
+                    vcAccountRequest.enableTimerProgress = false
+                    vcAccountRequest.enableAddAccount = true
+                    vcAccountRequest.delegate = self
+                    vcAccountRequest.dismissDidEnterBackground = true
+                    
+                    let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
+                    let numberCell = accounts.count + 1
+                    let height = min(CGFloat(numberCell * Int(vcAccountRequest.heightCell) + 45), screenHeighMax)
+                    
+                    let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height)
+                    
+                    UIApplication.shared.keyWindow?.rootViewController?.present(popup, animated: true)
                 }
-                navigationItem.setLeftBarButton(UIBarButtonItem(customView: button), animated: true)
-                navigationItem.leftItemsSupplementBackButton = true
             }
         }
+        navigationItem.setLeftBarButton(UIBarButtonItem(customView: button), animated: true)
+        navigationItem.leftItemsSupplementBackButton = true
     }
 
     func getNavigationTitle() -> String {
@@ -729,16 +720,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     // MARK: - TAP EVENT
 
-    @objc func tapSelect(sender: Any) {
-
-        isEditMode = !isEditMode
-
-        selectOcId.removeAll()
-        setNavigationItem()
-
-        self.collectionView.reloadData()
-    }
-
     func accountRequestChangeAccount(account: String) {
         NCManageDatabase.shared.setAccountActive(account)
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
@@ -787,11 +768,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         sortMenu.toggleMenu(viewController: self, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
     }
 
-    @objc func tapSelectMenu(sender: Any) {
-
-        toggleMenuSelect()
-    }
-
     func tapMoreHeader(sender: Any) { }
 
     func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) {
@@ -1228,7 +1204,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
                 return
             }
 
-            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+            if CCUtility.fileProviderStorageExists(metadata) {
                 NCViewer.shared.view(viewController: self, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
             } else if NCCommunication.shared.isNetworkReachable() {
                 NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileView) { _ in }
@@ -1245,12 +1221,6 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
         navigationController?.pushViewController(viewController, animated: true)
     }
 
-    func collectionViewSelectAll() {
-        selectOcId = dataSource.metadatas.map({ $0.ocId })
-        navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(dataSource.metadatas.count)"
-        collectionView.reloadData()
-    }
-
     @available(iOS 13.0, *)
     func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
 
@@ -1459,7 +1429,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                 // image local
                 if dataSource.metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                } else if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
                 }
             }
@@ -1627,7 +1597,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                 // image Local
                 if dataSource.metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                } else if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
                 }
             }

+ 3 - 2
iOSClient/Main/Collection Common/NCGridCell.swift

@@ -23,7 +23,8 @@
 
 import UIKit
 
-class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
+class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol, NCTrashCell {
+    var labelInfo: UILabel?
 
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageSelect: UIImageView!
@@ -35,7 +36,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     @IBOutlet weak var imageVisualEffect: UIVisualEffectView!
     @IBOutlet weak var progressView: UIProgressView!
 
-    private var objectId = ""
+    internal var objectId = ""
     private var user = ""
 
     weak var delegate: NCGridCellDelegate?

+ 130 - 0
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 <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import NCCommunication
+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
+    }
+}
+
+protocol NCSelectableNavigationView: AnyObject {
+    var appDelegate: AppDelegate { get }
+    var selectableDataSource: [RealmSwiftObject] { get }
+    var collectionView: UICollectionView! { get set }
+    var isEditMode: Bool { get set }
+    var selectOcId: [String] { get set }
+    var titleCurrentFolder: String { get }
+    var navigationItem: UINavigationItem { get }
+
+    var selectActions: [NCMenuAction] { get }
+
+    func reloadDataSource()
+    func setNavigationItem()
+
+    func tapSelectMenu()
+    func tapSelect()
+}
+
+extension NCSelectableNavigationView {
+    func setNavigationItem() { setNavigationHeader() }
+
+    func setNavigationHeader() {
+        if isEditMode {
+            navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "navigationMore"), style: .plain, action: tapSelectMenu)
+            navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .plain, action: tapSelect)
+            navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(selectableDataSource.count)"
+        } else {
+            navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain, action: tapSelect)
+            navigationItem.leftBarButtonItem = nil
+            navigationItem.title = titleCurrentFolder
+        }
+    }
+
+    func tapSelect() {
+        isEditMode = !isEditMode
+        selectOcId.removeAll()
+        self.setNavigationItem()
+        self.collectionView.reloadData()
+    }
+
+    func collectionViewSelectAll() {
+        selectOcId = selectableDataSource.compactMap({ $0.primaryKeyValue })
+        navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(selectableDataSource.count)"
+        collectionView.reloadData()
+    }
+}
+
+extension NCSelectableNavigationView where Self: UIViewController {
+    func tapSelectMenu() {
+        presentMenu(with: selectActions)
+    }
+
+    var selectActions: [NCMenuAction] {
+        var actions = [NCMenuAction]()
+        if selectOcId.count != selectableDataSource.count {
+            actions.append(.selectAllAction(action: collectionViewSelectAll))
+        }
+
+        guard !selectOcId.isEmpty else { return actions }
+        var selectedMetadatas: [tableMetadata] = []
+        var selectedMediaMetadatas: [tableMetadata] = []
+        var isAnyOffline = false
+
+        for ocId in selectOcId {
+            guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
+            selectedMetadatas.append(metadata)
+            if [NCCommunicationCommon.typeClassFile.image.rawValue, NCCommunicationCommon.typeClassFile.video.rawValue].contains(metadata.classFile) {
+                selectedMediaMetadatas.append(metadata)
+            }
+
+            guard !isAnyOffline else { continue }
+            if metadata.directory,
+               let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
+                isAnyOffline = directory.offline
+            } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+                isAnyOffline = localFile.offline
+            } // else: file is not offline, continue
+        }
+
+        actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
+
+        if !selectedMediaMetadatas.isEmpty {
+            actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect))
+        }
+        actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: {
+            self.reloadDataSource()
+            self.tapSelect()
+        }))
+
+        actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, completion: tapSelect))
+        actions.append(.copyAction(selectOcId: selectOcId, hudView: self.view, completion: tapSelect))
+        actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
+        return actions
+    }
+}

+ 3 - 0
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -274,6 +274,9 @@ import XLForm
             return
         } else {
 
+            //Trim whitespaces after checks above
+            fileNameForm = (fileNameForm as! String).trimmingCharacters(in: .whitespacesAndNewlines)
+
             let result = NCCommunicationCommon.shared.getInternalType(fileName: fileNameForm as! String, mimeType: "", directory: false)
             if NCUtility.shared.isDirectEditing(account: appDelegate.account, contentType: result.mimeType).count == 0 {
                 fileNameForm = (fileNameForm as! NSString).deletingPathExtension + "." + fileNameExtension

+ 3 - 3
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -762,10 +762,10 @@ class NCCreateScanDocument: NSObject, VNDocumentCameraViewControllerDelegate {
         }
 
         controller.dismiss(animated: true) {
-            if self.viewController is DragDropViewController {
-                (self.viewController as! DragDropViewController).loadImage()
+            if let viewController = self.viewController as? NCScan {
+                viewController.loadImage()
             } else {
-                let storyboard = UIStoryboard(name: "Scan", bundle: nil)
+                let storyboard = UIStoryboard(name: "NCScan", bundle: nil)
                 let controller = storyboard.instantiateInitialViewController()!
 
                 controller.modalPresentationStyle = UIModalPresentationStyle.pageSheet

+ 15 - 19
iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard

@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hTm-e0-ORl">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hTm-e0-ORl">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -19,18 +17,15 @@
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="eeP-9N-ZRP">
-                                <rect key="frame" x="0.0" y="89" width="375" height="150"/>
+                                <rect key="frame" x="0.0" y="163" width="375" height="504"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                <constraints>
-                                    <constraint firstAttribute="height" constant="150" id="kKy-VZ-d0g"/>
-                                </constraints>
                                 <connections>
                                     <outlet property="dataSource" destination="uQo-FX-ejX" id="Y7U-AM-3WZ"/>
                                     <outlet property="delegate" destination="uQo-FX-ejX" id="gsE-cc-f9G"/>
                                 </connections>
                             </tableView>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s98-hk-uUP">
-                                <rect key="frame" x="150" y="249" width="75" height="75"/>
+                                <rect key="frame" x="150" y="54" width="75" height="75"/>
                                 <constraints>
                                     <constraint firstAttribute="width" constant="75" id="h48-gg-iPB"/>
                                     <constraint firstAttribute="height" constant="75" id="mjB-VI-Gzf"/>
@@ -41,7 +36,7 @@
                                 </connections>
                             </button>
                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="we1-Q7-8Us">
-                                <rect key="frame" x="20" y="344" width="70" height="17"/>
+                                <rect key="frame" x="20" y="132.5" width="70" height="17"/>
                                 <constraints>
                                     <constraint firstAttribute="width" constant="70" id="Su3-nN-I5z"/>
                                 </constraints>
@@ -50,7 +45,7 @@
                                 <nil key="highlightedColor"/>
                             </label>
                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zd0-7e-z9V" userLabel="Label Duration">
-                                <rect key="frame" x="285" y="344" width="70" height="17"/>
+                                <rect key="frame" x="285" y="132.5" width="70" height="17"/>
                                 <constraints>
                                     <constraint firstAttribute="width" constant="70" id="JZa-Lm-wgx"/>
                                 </constraints>
@@ -59,24 +54,25 @@
                                 <nil key="highlightedColor"/>
                             </label>
                             <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="6HO-NK-obf">
-                                <rect key="frame" x="100" y="351.5" width="175" height="2"/>
+                                <rect key="frame" x="100" y="139" width="175" height="4"/>
                             </progressView>
                         </subviews>
+                        <viewLayoutGuide key="safeArea" id="ILQ-5j-b92"/>
                         <constraints>
-                            <constraint firstItem="eeP-9N-ZRP" firstAttribute="top" secondItem="ILQ-5j-b92" secondAttribute="top" constant="25" id="AEH-dn-vHh"/>
+                            <constraint firstItem="s98-hk-uUP" firstAttribute="top" secondItem="ILQ-5j-b92" secondAttribute="top" constant="10" id="5mu-uh-SqU"/>
                             <constraint firstItem="6HO-NK-obf" firstAttribute="leading" secondItem="we1-Q7-8Us" secondAttribute="trailing" constant="10" id="C6M-oc-T1Z"/>
                             <constraint firstItem="6HO-NK-obf" firstAttribute="centerY" secondItem="we1-Q7-8Us" secondAttribute="centerY" id="J28-Wh-Byv"/>
+                            <constraint firstItem="6HO-NK-obf" firstAttribute="top" secondItem="s98-hk-uUP" secondAttribute="bottom" constant="10" id="RxM-5b-ZEm"/>
+                            <constraint firstItem="zd0-7e-z9V" firstAttribute="centerY" secondItem="6HO-NK-obf" secondAttribute="centerY" id="T4r-QI-wSb"/>
                             <constraint firstItem="ILQ-5j-b92" firstAttribute="trailing" secondItem="zd0-7e-z9V" secondAttribute="trailing" constant="20" id="Zkq-S4-Hnu"/>
+                            <constraint firstItem="ILQ-5j-b92" firstAttribute="bottom" secondItem="eeP-9N-ZRP" secondAttribute="bottom" id="a74-Md-bui"/>
                             <constraint firstItem="zd0-7e-z9V" firstAttribute="leading" secondItem="6HO-NK-obf" secondAttribute="trailing" constant="10" id="bKe-sM-Gvy"/>
-                            <constraint firstItem="we1-Q7-8Us" firstAttribute="top" secondItem="s98-hk-uUP" secondAttribute="bottom" constant="20" id="ehi-c7-6om"/>
-                            <constraint firstItem="zd0-7e-z9V" firstAttribute="top" secondItem="s98-hk-uUP" secondAttribute="bottom" constant="20" id="elb-sf-8LP"/>
-                            <constraint firstItem="s98-hk-uUP" firstAttribute="top" secondItem="eeP-9N-ZRP" secondAttribute="bottom" constant="10" id="o8u-gE-61X"/>
+                            <constraint firstItem="eeP-9N-ZRP" firstAttribute="top" secondItem="6HO-NK-obf" secondAttribute="bottom" constant="20" id="eqV-Ls-16q"/>
                             <constraint firstItem="eeP-9N-ZRP" firstAttribute="leading" secondItem="ILQ-5j-b92" secondAttribute="leading" id="pNx-zH-54E"/>
                             <constraint firstItem="we1-Q7-8Us" firstAttribute="leading" secondItem="ILQ-5j-b92" secondAttribute="leading" constant="20" id="uvM-m5-Ofp"/>
                             <constraint firstItem="s98-hk-uUP" firstAttribute="centerX" secondItem="ILQ-5j-b92" secondAttribute="centerX" id="vLn-iu-xOz"/>
                             <constraint firstItem="ILQ-5j-b92" firstAttribute="trailing" secondItem="eeP-9N-ZRP" secondAttribute="trailing" id="yxz-bK-MTp"/>
                         </constraints>
-                        <viewLayoutGuide key="safeArea" id="ILQ-5j-b92"/>
                     </view>
                     <navigationItem key="navigationItem" id="YB9-Lg-X9d"/>
                     <connections>
@@ -97,7 +93,7 @@
                 <navigationController automaticallyAdjustsScrollViewInsets="NO" id="hTm-e0-ORl" sceneMemberID="viewController">
                     <toolbarItems/>
                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="erz-WY-qOP">
-                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
                         <autoresizingMask key="autoresizingMask"/>
                     </navigationBar>
                     <nil name="viewControllers"/>

+ 227 - 259
iOSClient/Main/NCFunctionCenter.swift

@@ -25,14 +25,13 @@ import UIKit
 import NCCommunication
 import Queuer
 import JGProgressHUD
+import SVGKit
 
 @objc class NCFunctionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelectDelegate {
     @objc public static let shared: NCFunctionCenter = {
         let instance = NCFunctionCenter()
-
         NotificationCenter.default.addObserver(instance, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
         NotificationCenter.default.addObserver(instance, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
-
         return instance
     }()
 
@@ -44,126 +43,121 @@ import JGProgressHUD
 
     @objc func downloadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let selector = userInfo["selector"] as? String, let errorCode = userInfo["errorCode"] as? Int, let errorDescription = userInfo["errorDescription"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-
-                if metadata.account != appDelegate.account { return }
-
-                if errorCode == 0 {
-
-                    switch selector {
-                    case NCGlobal.shared.selectorLoadFileQuickLook:
-
-                        let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
-                        CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath)
-
-                        var editingMode = false
-                        if #available(iOS 13.0, *) {
-                            editingMode = true
-                        }
-
-                        let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: editingMode, metadata: metadata)
-                        let navigationController = UINavigationController(rootViewController: viewerQuickLook)
-                        navigationController.modalPresentationStyle = .overFullScreen
-
-                        self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
-
-                    case NCGlobal.shared.selectorLoadFileView:
-
-                        if UIApplication.shared.applicationState == UIApplication.State.active {
-
-                            if metadata.contentType.contains("opendocument") && !NCUtility.shared.isRichDocument(metadata) {
-
-                                self.openDocumentController(metadata: metadata)
-
-                            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.compress.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
-
-                                self.openDocumentController(metadata: metadata)
-
-                            } else {
-
-                                if let viewController = self.appDelegate.activeViewController {
-                                    let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
-                                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
-                                }
-                            }
-                        }
-
-                    case NCGlobal.shared.selectorOpenIn:
-
-                        if UIApplication.shared.applicationState == UIApplication.State.active {
-
-                            self.openDocumentController(metadata: metadata)
-                        }
-
-                    case NCGlobal.shared.selectorLoadCopy:
-
-                        copyPasteboard()
-
-                    case NCGlobal.shared.selectorLoadOffline:
-
-                        NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: true)
-
-                    case NCGlobal.shared.selectorPrint:
-
-                        printDocument(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveAlbum:
-
-                        saveAlbum(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveBackground:
-
-                        saveBackground(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
-
-                        var metadata = metadata
-                        var metadataMOV = metadata
-                        guard let metadataTMP = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) else { break }
-
-                        if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoIMG {
-                            metadataMOV = metadataTMP
-                        }
-
-                        if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoMOV {
-                            metadata = metadataTMP
-                        }
-
-                        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && CCUtility.fileProviderStorageExists(metadataMOV.ocId, fileNameView: metadataMOV.fileNameView) {
-                            saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
-                        }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let selector = userInfo["selector"] as? String,
+              let errorCode = userInfo["errorCode"] as? Int,
+              let errorDescription = userInfo["errorDescription"] as? String,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              metadata.account == appDelegate.account
+        else { return }
+
+        guard errorCode == 0 else {
+            // File do not exists on server, remove in local
+            if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse {
+                do {
+                    try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+                } catch { }
+                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                
+            } else {
+                NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
+            }
+            return
+        }
+        
+        switch selector {
+        case NCGlobal.shared.selectorLoadFileQuickLook:
+            let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
+            CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath)
+
+            var editingMode = false
+            if #available(iOS 13.0, *) {
+                editingMode = true
+            }
 
-                    case NCGlobal.shared.selectorSaveAsScan:
+            let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: editingMode, metadata: metadata)
+            let navigationController = UINavigationController(rootViewController: viewerQuickLook)
+            navigationController.modalPresentationStyle = .overFullScreen
 
-                        saveAsScan(metadata: metadata)
+            self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
 
-                    case NCGlobal.shared.selectorOpenDetail:
+        case NCGlobal.shared.selectorLoadFileView:
+            guard UIApplication.shared.applicationState == UIApplication.State.active else { break }
 
-                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId])
+            if metadata.contentType.contains("opendocument") && !NCUtility.shared.isRichDocument(metadata) {
+                self.openDocumentController(metadata: metadata)
+            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.compress.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
+                self.openDocumentController(metadata: metadata)
+            } else {
+                if let viewController = self.appDelegate.activeViewController {
+                    let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
+                }
+            }
+            
+        case NCGlobal.shared.selectorOpenIn:
+            if UIApplication.shared.applicationState == UIApplication.State.active {
+                self.openDocumentController(metadata: metadata)
+            }
+            
+        case NCGlobal.shared.selectorLoadOffline:
+            NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: true)
+            
+        case NCGlobal.shared.selectorPrint:
+            printDocument(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveAlbum:
+            saveAlbum(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveBackground:
+            saveBackground(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
 
-                    default:
+            var metadata = metadata
+            var metadataMOV = metadata
+            guard let metadataTMP = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) else { break }
 
-                        break
-                    }
+            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoIMG {
+                metadataMOV = metadataTMP
+            }
 
-                } else {
+            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoMOV {
+                metadata = metadataTMP
+            }
 
-                    // File do not exists on server, remove in local
-                    if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse {
+            if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
+                saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
+            }
 
-                        do {
-                            try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
-                        } catch { }
+        case NCGlobal.shared.selectorSaveAsScan:
+            saveAsScan(metadata: metadata)
 
-                        NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                        NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+        case NCGlobal.shared.selectorOpenDetail:
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId])
 
-                    } else {
+        default:
+            break
+        }
+    }
 
-                        NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
-                    }
-                }
+    func setMetadataAvalableOffline(_ metadata: tableMetadata, isOffline: Bool) {
+        let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+        if isOffline {
+            if metadata.directory {
+                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
+            } else {
+                NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
+            }
+        } else if metadata.directory {
+            NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
+            NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
+        } else {
+            NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
+            if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
+                NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
             }
         }
     }
@@ -204,7 +198,7 @@ import JGProgressHUD
 
     func openDownload(metadata: tableMetadata, selector: String) {
 
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+        if CCUtility.fileProviderStorageExists(metadata) {
 
             NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": 0, "errorDescription": "" ])
 
@@ -225,47 +219,35 @@ import JGProgressHUD
         documentController?.presentOptionsMenu(from: mainTabBar.menuRect, in: mainTabBar, animated: true)
     }
 
-    func openActivityViewController(selectOcId: [String]) {
-
-        NCUtility.shared.startActivityIndicator(backgroundView: nil, blurEffect: true)
-
-        var error: Int = 0
-        var items: [Any] = []
+    func openActivityViewController(selectedMetadata: [tableMetadata]) {
+        let metadatas = selectedMetadata.filter({ !$0.directory })
+        var items: [URL] = []
+        var downloadMetadata: [(tableMetadata, URL)] = []
 
-        for ocId in selectOcId {
-            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                if metadata.directory {
-                    continue
-                }
-                if !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                    let semaphore = Semaphore()
-                    NCNetworking.shared.download(metadata: metadata, selector: "") { errorCode in
-                        error = errorCode
-                        semaphore.continue()
-                    }
-                    semaphore.wait()
-                }
-                if error != 0 {
-                    break
-                }
-                let fileURL = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
-                items.append(fileURL)
+        for metadata in metadatas {
+            let fileURL = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+            if CCUtility.fileProviderStorageExists(metadata) { items.append(fileURL) }
+            else { downloadMetadata.append((metadata, fileURL)) }
+        }
+
+        let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadata.count, hudView: self.appDelegate.window?.rootViewController?.view)
+        for (metadata, url) in downloadMetadata {
+            processor.execute { completion in
+                NCNetworking.shared.download(metadata: metadata, selector: "", completion: { _ in
+                    if CCUtility.fileProviderStorageExists(metadata) { items.append(url) }
+                    completion()
+                })
             }
         }
-        if error == 0 && items.count > 0 {
-
-            guard let mainTabBar = self.appDelegate.mainTabBar else { return }
 
+        processor.completeWork {
+            guard !items.isEmpty, let mainTabBar = self.appDelegate.mainTabBar else { return }
             let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
-
             activityViewController.popoverPresentationController?.permittedArrowDirections = .any
             activityViewController.popoverPresentationController?.sourceView = mainTabBar
             activityViewController.popoverPresentationController?.sourceRect = mainTabBar.menuRect
-
             self.appDelegate.window?.rootViewController?.present(activityViewController, animated: true)
-
         }
-        NCUtility.shared.stopActivityIndicator()
     }
 
     // MARK: - Save as scan
@@ -278,7 +260,7 @@ import JGProgressHUD
 
         NCUtilityFileSystem.shared.copyFile(atPath: fileNamePath, toPath: fileNamePathDestination)
 
-        let storyboard = UIStoryboard(name: "Scan", bundle: nil)
+        let storyboard = UIStoryboard(name: "NCScan", bundle: nil)
         let navigationController = storyboard.instantiateInitialViewController()!
 
         navigationController.modalPresentationStyle = UIModalPresentationStyle.pageSheet
@@ -289,21 +271,37 @@ import JGProgressHUD
     // MARK: - Print
 
     func printDocument(metadata: tableMetadata) {
-
         let fileNameURL = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!)
+        let printController = UIPrintInteractionController.shared
+        let printInfo = UIPrintInfo(dictionary: nil)
+        printInfo.jobName = fileNameURL.lastPathComponent
+        printInfo.outputType = metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue ? .photo : .general
+        printController.printInfo = printInfo
+        printController.showsNumberOfCopies = true
+
+        guard !UIPrintInteractionController.canPrint(fileNameURL) else {
+            printController.printingItem = fileNameURL
+            printController.present(animated: true)
+            return
+        }
 
-        if UIPrintInteractionController.canPrint(fileNameURL) {
-
-            let printInfo = UIPrintInfo(dictionary: nil)
-            printInfo.jobName = fileNameURL.lastPathComponent
-            printInfo.outputType = .photo
+        // can't print without data
+        guard let data = try? Data(contentsOf: fileNameURL) else { return }
 
-            let printController = UIPrintInteractionController.shared
-            printController.printInfo = printInfo
-            printController.showsNumberOfCopies = true
-            printController.printingItem = fileNameURL
-            printController.present(animated: true, completionHandler: nil)
+        if let svg = SVGKImage(data: data) {
+            printController.printingItem = svg.uiImage
+            printController.present(animated: true)
+            return
         }
+
+        guard let text = String(data: data, encoding: .utf8) else { return }
+        let formatter = UISimpleTextPrintFormatter(text: text)
+        formatter.perPageContentInsets.top = 72
+        formatter.perPageContentInsets.bottom = 72
+        formatter.perPageContentInsets.left = 72
+        formatter.perPageContentInsets.right = 72
+        printController.printFormatter = formatter
+        printController.present(animated: true)
     }
 
     // MARK: - Save photo
@@ -344,15 +342,15 @@ import JGProgressHUD
 
     func saveLivePhoto(metadata: tableMetadata, metadataMOV: tableMetadata) {
 
-        if !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+        if !CCUtility.fileProviderStorageExists(metadata) {
             NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbumLivePhotoIMG)
         }
 
-        if !CCUtility.fileProviderStorageExists(metadataMOV.ocId, fileNameView: metadataMOV.fileNameView) {
+        if !CCUtility.fileProviderStorageExists(metadataMOV) {
             NCOperationQueue.shared.download(metadata: metadataMOV, selector: NCGlobal.shared.selectorSaveAlbumLivePhotoMOV)
         }
 
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && CCUtility.fileProviderStorageExists(metadataMOV.ocId, fileNameView: metadataMOV.fileNameView) {
+        if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
             saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
         }
     }
@@ -418,100 +416,74 @@ import JGProgressHUD
 
     // MARK: - Copy & Paste
 
-    func copyPasteboard() {
-
-        var metadatas: [tableMetadata] = []
+    func copyPasteboard(pasteboardOcIds: [String], hudView: UIView) {
         var items = [[String: Any]]()
-
-        for ocId in appDelegate.pasteboardOcIds {
-            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                metadatas.append(metadata)
+        let hud = JGProgressHUD()
+        hud.textLabel.text = NSLocalizedString("_wait_", comment: "")
+        hud.show(in: hudView)
+
+        // getting file data can take some time and block the main queue
+        DispatchQueue.global(qos: .userInitiated).async {
+            var downloadMetadatas: [tableMetadata] = []
+            for ocid in pasteboardOcIds {
+                guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocid) else { continue }
+                if let pasteboardItem = metadata.toPasteBoardItem() { items.append(pasteboardItem) }
+                else { downloadMetadatas.append(metadata) }
             }
-        }
 
-        for metadata in metadatas {
+            DispatchQueue.main.async(execute: hud.dismiss)
 
-            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                do {
-                    // Get Data
-                    let data = try Data(contentsOf: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)))
-                    // Pasteboard item
-                    if let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (metadata.fileNameView as NSString).pathExtension as CFString, nil) {
-                        let fileUTI = unmanagedFileUTI.takeRetainedValue() as String
-                        items.append([fileUTI: data])
-                    }
-                } catch {
-                    print("error")
+            // do 5 downloads in parallel to optimize efficiency
+            let parallelizer = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, hudView: hudView)
+
+            for metadata in downloadMetadatas {
+                parallelizer.execute { completion in
+                    NCNetworking.shared.download(metadata: metadata, selector: "") { _ in completion() }
                 }
-            } else {
-                NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadCopy) { _ in }
+            }
+            parallelizer.completeWork {
+                items.append(contentsOf: downloadMetadatas.compactMap({ $0.toPasteBoardItem() }))
+                UIPasteboard.general.setItems(items, options: [:])
             }
         }
+    }
 
-        UIPasteboard.general.setItems(items, options: [:])
+    func upload(fileName: String, serverUrlFileName: String, fileNameLocalPath: String, serverUrl: String, completion: @escaping () -> Void) {
+        NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath) { _ in
+        } progressHandler: { progress in
+        } completionHandler: { account, ocId, etag, _, _, _, errorCode, errorDescription in
+            if errorCode == 0 && etag != nil && ocId != nil {
+                let toPath = CCUtility.getDirectoryProviderStorageOcId(ocId!, fileNameView: fileName)!
+                NCUtilityFileSystem.shared.moveFile(atPath: fileNameLocalPath, toPath: toPath)
+                NCManageDatabase.shared.addLocalFile(account: account, etag: etag!, ocId: ocId!, fileName: fileName)
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl])
+            } else {
+                NCContentPresenter.shared.showError(description: errorDescription, errorCode: errorCode)
+            }
+            completion()
+        }
     }
 
     func pastePasteboard(serverUrl: String) {
+        let parallelizer = ParallelWorker(n: 5, titleKey: "_uploading_", totalTasks: nil, hudView: appDelegate.window?.rootViewController?.view)
 
-        var pasteboardTypes: [String] = []
-
-        func upload(pasteboardType: String?, data: Data?) -> Bool {
-
-            guard let data = data else { return false}
-            guard let pasteboardType = pasteboardType else { return false }
-
-            let results = NCCommunicationCommon.shared.getFileProperties(inUTI: pasteboardType as CFString)
-            if results.ext == "" { return false }
-
-            do {
+        for (index, items) in UIPasteboard.general.items.enumerated() {
+            for item in items {
+                let results = NCCommunicationCommon.shared.getFileProperties(inUTI: item.key as CFString)
+                guard !results.ext.isEmpty,
+                      let data = UIPasteboard.general.data(forPasteboardType: item.key, inItemSet: IndexSet([index]))?.first
+                else { continue }
                 let fileName = results.name + "_" + CCUtility.getIncrementalNumber() + "." + results.ext
                 let serverUrlFileName = serverUrl + "/" + fileName
                 let ocIdUpload = UUID().uuidString
                 let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(ocIdUpload, fileNameView: fileName)!
-                try data.write(to: URL(fileURLWithPath: fileNameLocalPath))
-                let hud = JGProgressHUD()
-                
-                hud.indicatorView = JGProgressHUDRingIndicatorView()
-                if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
-                    indicatorView.ringWidth = 1.5
-                }
-                hud.show(in: (appDelegate.window?.rootViewController?.view)!)
-                hud.textLabel.text = fileName
-
-                NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath) { _ in
-                } progressHandler: { progress in
-                    hud.progress = Float(progress.fractionCompleted)
-                } completionHandler: { account, ocId, etag, _, _, _, errorCode, errorDescription in
-                    if errorCode == 0 && etag != nil && ocId != nil {
-                        let toPath = CCUtility.getDirectoryProviderStorageOcId(ocId!, fileNameView: fileName)!
-                        NCUtilityFileSystem.shared.moveFile(atPath: fileNameLocalPath, toPath: toPath)
-                        NCManageDatabase.shared.addLocalFile(account: account, etag: etag!, ocId: ocId!, fileName: fileName)
-                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl])
-                        hud.indicatorView = JGProgressHUDSuccessIndicatorView()
-                        hud.textLabel.text = NSLocalizedString("_success_", comment: "")
-                    } else {
-                        hud.indicatorView = JGProgressHUDErrorIndicatorView()
-                        hud.textLabel.text = NSLocalizedString(errorDescription, comment: "")
-                    }
-                    hud.dismiss(afterDelay: 1)
-                }
-            } catch {
-                return false
-            }
-            return true
-        }
-
-        for (index, items) in UIPasteboard.general.items.enumerated() {
-
-            for item in items { pasteboardTypes.append(item.key) }
-
-            for typeIdentifier in pasteboardTypes {
-                let data = UIPasteboard.general.data(forPasteboardType: typeIdentifier, inItemSet: IndexSet([index]))?.first
-                if upload(pasteboardType: typeIdentifier, data: data) {
-                    continue
+                do { try data.write(to: URL(fileURLWithPath: fileNameLocalPath)) } catch { continue }
+                parallelizer.execute { completion in
+                    self.upload(fileName: fileName, serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, serverUrl: serverUrl, completion: completion)
                 }
             }
         }
+        parallelizer.completeWork()
     }
 
     // MARK: -
@@ -575,7 +547,7 @@ import JGProgressHUD
         }
     }
 
-    func openSelectView(items: [Any], viewController: UIViewController) {
+    func openSelectView(items: [Any]) {
 
         let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as! UINavigationController
         let topViewController = navigationController.topViewController as! NCSelect
@@ -623,7 +595,7 @@ import JGProgressHUD
         navigationController.setViewControllers(listViewController, animated: false)
         navigationController.modalPresentationStyle = .formSheet
 
-        viewController.present(navigationController, animated: true, completion: nil)
+        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
     }
 
     // MARK: - Context Menu Configuration
@@ -658,8 +630,7 @@ import JGProgressHUD
         let titleOffline = isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: "")
 
         let copy = UIAction(title: NSLocalizedString("_copy_file_", comment: ""), image: UIImage(systemName: "doc.on.doc")) { _ in
-            self.appDelegate.pasteboardOcIds = [metadata.ocId]
-            self.copyPasteboard()
+            self.copyPasteboard(pasteboardOcIds: [metadata.ocId], hudView: viewController.view)
         }
 
         let copyPath = UIAction(title: NSLocalizedString("_copy_path_", comment: ""), image: UIImage(systemName: "doc.on.clipboard")) { _ in
@@ -673,26 +644,9 @@ import JGProgressHUD
         }
 
         let offline = UIAction(title: titleOffline, image: UIImage(systemName: "tray.and.arrow.down")) { _ in
-            if isOffline {
-                if metadata.directory {
-                    NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
-                } else {
-                    NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
-                }
-            } else {
-                if metadata.directory {
-                    NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
-                    NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
-                } else {
-                    NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                    if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                        NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                    }
-                }
-            }
-
-            if viewController is NCCollectionViewCommon {
-                (viewController as! NCCollectionViewCommon).reloadDataSource()
+            self.setMetadataAvalableOffline(metadata, isOffline: isOffline)
+            if let viewController = viewController as? NCCollectionViewCommon {
+                viewController.reloadDataSource()
             }
         }
 
@@ -700,7 +654,7 @@ import JGProgressHUD
             if metadataMOV != nil {
                 self.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV!)
             } else {
-                if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                if CCUtility.fileProviderStorageExists(metadata) {
                     self.saveAlbum(metadata: metadata)
                 } else {
                     NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
@@ -709,7 +663,7 @@ import JGProgressHUD
         }
 
         let saveBackground = UIAction(title: NSLocalizedString("_use_as_background_", comment: ""), image: UIImage(systemName: "text.below.photo")) { _ in
-            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+            if CCUtility.fileProviderStorageExists(metadata) {
                 self.saveBackground(metadata: metadata)
             } else {
                 NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveBackground)
@@ -739,7 +693,7 @@ import JGProgressHUD
         // let open = UIMenu(title: NSLocalizedString("_open_", comment: ""), image: UIImage(systemName: "square.and.arrow.up"), children: [openIn, openQuickLook])
 
         let moveCopy = UIAction(title: NSLocalizedString("_move_or_copy_", comment: ""), image: UIImage(systemName: "arrow.up.right.square")) { _ in
-            self.openSelectView(items: [metadata], viewController: viewController)
+            self.openSelectView(items: [metadata])
         }
 
         let rename = UIAction(title: NSLocalizedString("_rename_", comment: ""), image: UIImage(systemName: "pencil")) { _ in
@@ -833,3 +787,17 @@ import JGProgressHUD
         return UIMenu(title: "", children: [detail, submenu])
     }
 }
+
+fileprivate extension tableMetadata {
+    func toPasteBoardItem() -> [String: Any]? {
+        // Get Data
+        let fileUrl = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView))
+        guard CCUtility.fileProviderStorageExists(self),
+              let data = try? Data(contentsOf: fileUrl),
+              let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil)
+        else { return nil }
+        // Pasteboard item
+        let fileUTI = unmanagedFileUTI.takeRetainedValue() as String
+        return [fileUTI: data]
+    }
+}

+ 6 - 0
iOSClient/Media/NCMedia.swift

@@ -133,6 +133,12 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
         }
     }
 
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        mediaCommandTitle()
+    }
+    
     override func viewWillDisappear(_ animated: Bool) {
         super.viewWillDisappear(animated)
 

+ 16 - 272
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -5,9 +5,11 @@
 //  Created by Philippe Weidmann on 24.01.20.
 //  Copyright © 2020 Philippe Weidmann. All rights reserved.
 //  Copyright © 2020 Marino Faggiana All rights reserved.
+//  Copyright © 2022 Henrik Storch. All rights reserved.
 //
 //  Author Philippe Weidmann
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -38,34 +40,13 @@ extension NCCollectionViewCommon {
         let serverUrl = metadata.serverUrl + "/" + metadata.fileName
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
-        var isOffline = false
+        let isOffline: Bool
 
-        var titleDelete = NSLocalizedString("_delete_", comment: "")
-        if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
-            titleDelete = NSLocalizedString("_leave_share_", comment: "")
-        } else if metadata.directory {
-            titleDelete = NSLocalizedString("_delete_folder_", comment: "")
-        } else {
-            titleDelete = NSLocalizedString("_delete_file_", comment: "")
-        }
-
-        if let metadataFolder = metadataFolder {
-            let isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
-            let isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
-            if isShare || isMounted {
-                titleDelete = NSLocalizedString("_leave_share_", comment: "")
-            }
-        }
-
-        if metadata.directory {
-            if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
-                isOffline = directory.offline
-            }
-        } else {
-            if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
-                isOffline = localFile.offline
-            }
-        }
+        if metadata.directory, let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
+            isOffline = directory.offline
+        } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+            isOffline = localFile.offline
+        } else { isOffline = false }
 
         let editors = NCUtility.shared.isDirectEditing(account: metadata.account, contentType: metadata.contentType)
         let isRichDocument = NCUtility.shared.isRichDocument(metadata)
@@ -126,32 +107,7 @@ extension NCCollectionViewCommon {
         // OFFLINE
         //
         if !isFolderEncrypted {
-            actions.append(
-                NCMenuAction(
-                    title: isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
-                    action: { _ in
-                        if isOffline {
-                            if metadata.directory {
-                                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
-                            } else {
-                                NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
-                            }
-                        } else {
-                            if metadata.directory {
-                                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
-                                NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
-                            } else {
-                                NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                                if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                                    NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                                }
-                            }
-                        }
-                        self.reloadDataSource()
-                    }
-                )
-            )
+            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: self.reloadDataSource))
         }
 
         //
@@ -190,67 +146,21 @@ extension NCCollectionViewCommon {
         // OPEN IN
         //
         if !metadata.directory && !NCBrandOptions.shared.disable_openin_file {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_open_in_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
-                    action: { menuAction in
-                        if self is NCFileViewInFolder {
-                            self.dismiss(animated: true) {
-                                NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorOpenIn)
-                            }
-                        } else {
-                            NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorOpenIn)
-                        }                        
-                    }
-                )
-            )
+            actions.append(.openInAction(selectedMetadatas: [metadata], viewController: self))
         }
 
         //
         // PRINT
         //
-        if (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && metadata.contentType != "image/svg+xml") || metadata.contentType == "application/pdf" || metadata.contentType == "com.adobe.pdf" {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_print_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "printer"),
-                    action: { _ in
-                        NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorPrint)
-                    }
-                )
-            )
+        if metadata.isPrintable {
+            actions.append(.printAction(metadata: metadata))
         }
 
         //
         // SAVE
         //
         if (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && metadata.contentType != "image/svg+xml") || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-            var title: String = NSLocalizedString("_save_selected_files_", comment: "")
-            var icon = NCUtility.shared.loadImage(named: "square.and.arrow.down")
-            let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata)
-            if metadataMOV != nil {
-                title = NSLocalizedString("_livephoto_save_", comment: "")
-                icon = NCUtility.shared.loadImage(named: "livephoto")
-            }
-
-            actions.append(
-                NCMenuAction(
-                    title: title,
-                    icon: icon,
-                    action: { _ in
-                        if metadataMOV != nil {
-                            NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV!)
-                        } else {
-                            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                                NCFunctionCenter.shared.saveAlbum(metadata: metadata)
-                            } else {
-                                NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
-                            }
-                        }
-                    }
-                )
-            )
+            actions.append(.saveMediaAction(selectedMediaMetadatas: [metadata]))
         }
 
         //
@@ -298,31 +208,14 @@ extension NCCollectionViewCommon {
         // COPY - MOVE
         //
         if !isFolderEncrypted && serverUrl != "" {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_move_or_copy_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
-                    action: { _ in
-                        NCFunctionCenter.shared.openSelectView(items: [metadata], viewController: self)
-                    }
-                )
-            )
+            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata]))
         }
 
         //
         // COPY
         //
         if !metadata.directory {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_copy_file_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
-                    action: { _ in
-                        self.appDelegate.pasteboardOcIds = [metadata.ocId]
-                        NCFunctionCenter.shared.copyPasteboard()
-                    }
-                )
-            )
+            actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: self.view))
         }
         
         /*
@@ -374,23 +267,7 @@ extension NCCollectionViewCommon {
         //
         // DELETE
         //
-        actions.append(
-            NCMenuAction(
-                title: titleDelete,
-                icon: NCUtility.shared.loadImage(named: "trash"),
-                action: { _ in
-                    let alertController = UIAlertController(title: "", message: metadata.fileNameView + "\n\n" + NSLocalizedString("_want_delete_", comment: ""), preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: false)
-                    })
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: true)
-                    })
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
-                    self.present(alertController, animated: true, completion: nil)
-                }
-            )
-        )
+        actions.append(.deleteAction(selectedMetadatas: [metadata], metadataFolder: metadataFolder, viewController: self))
 
         //
         // SET FOLDER E2EE
@@ -444,137 +321,4 @@ extension NCCollectionViewCommon {
 
         presentMenu(with: actions)
     }
-
-    func toggleMenuSelect() {
-
-        var actions = [NCMenuAction]()
-
-        //
-        // SELECT ALL
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_select_all_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "checkmark.circle.fill"),
-                action: { _ in
-                    self.collectionViewSelectAll()
-                }
-            )
-        )
-
-        //
-        // OPEN IN
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_open_in_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
-                action: { _ in
-                    NCFunctionCenter.shared.openActivityViewController(selectOcId: self.selectOcId)
-                    self.tapSelect(sender: self)
-                }
-            )
-        )
-
-        //
-        // SAVE TO PHOTO GALLERY
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_save_selected_files_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "square.and.arrow.down"),
-                action: { _ in
-                    for ocId in self.selectOcId {
-                        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-                                if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                                    NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
-                                } else {
-                                    if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                                        NCFunctionCenter.shared.saveAlbum(metadata: metadata)
-                                    } else {
-                                        NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    self.tapSelect(sender: self)
-                }
-            )
-        )
-
-        //
-        // COPY - MOVE
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
-                action: { _ in
-                    var meradatasSelect = [tableMetadata]()
-                    for ocId in self.selectOcId {
-                        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                            meradatasSelect.append(metadata)
-                        }
-                    }
-                    if meradatasSelect.count > 0 {
-                        NCFunctionCenter.shared.openSelectView(items: meradatasSelect, viewController: self)
-                    }
-                    self.tapSelect(sender: self)
-                }
-            )
-        )
-
-        //
-        // COPY
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_copy_file_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
-                action: { _ in
-                    self.appDelegate.pasteboardOcIds.removeAll()
-                    for ocId in self.selectOcId {
-                        self.appDelegate.pasteboardOcIds.append(ocId)
-                    }
-                    NCFunctionCenter.shared.copyPasteboard()
-                    self.tapSelect(sender: self)
-                }
-            )
-        )
-
-        //
-        // DELETE
-        //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_delete_selected_files_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "trash"),
-                action: { _ in
-                    let alertController = UIAlertController(title: "", message: NSLocalizedString("_want_delete_", comment: ""), preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        for ocId in self.selectOcId {
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: false)
-                            }
-                        }
-                        self.tapSelect(sender: self)
-                    })
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        for ocId in self.selectOcId {
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: true)
-                            }
-                        }
-                        self.tapSelect(sender: self)
-                    })
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
-                    self.present(alertController, animated: true, completion: nil)
-                }
-            )
-        )
-
-        presentMenu(with: actions)
-    }
 }

+ 16 - 101
iOSClient/Menu/NCMedia+Menu.swift

@@ -26,11 +26,18 @@ import FloatingPanel
 import NCCommunication
 
 extension NCMedia {
+    func tapSelect() {
+        self.isEditMode = false
+        self.selectOcId.removeAll()
+        self.reloadDataThenPerform { }
+    }
 
     func toggleMenu() {
 
         var actions: [NCMenuAction] = []
 
+        defer { presentMenu(with: actions) }
+
         if !isEditMode {
             if metadatas.count > 0 {
                 actions.append(
@@ -137,129 +144,37 @@ extension NCMedia {
                 NCMenuAction(
                     title: NSLocalizedString("_cancel_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "xmark"),
-                    action: { _ in
-                        self.isEditMode = false
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
+                    action: { _ in self.tapSelect() }
                 )
             )
 
+            guard !selectOcId.isEmpty else { return }
+            let selectedMetadatas = selectOcId.compactMap(NCManageDatabase.shared.getMetadataFromOcId)
+
             //
             // OPEN IN
             //
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_open_in_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
-                    action: { _ in
-                        self.isEditMode = false
-                        NCFunctionCenter.shared.openActivityViewController(selectOcId: self.selectOcId)
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
-                )
-            )
+            actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
 
             //
             // SAVE TO PHOTO GALLERY
             //
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_save_selected_files_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "square.and.arrow.down"),
-                    action: { _ in
-                        self.isEditMode = false
-                        for ocId in self.selectOcId {
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-                                    if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                                        NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
-                                    } else {
-                                        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                                            NCFunctionCenter.shared.saveAlbum(metadata: metadata)
-                                        } else {
-                                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
-                )
-            )
+            actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMetadatas, completion: tapSelect))
 
             //
             // COPY - MOVE
             //
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
-                    action: { _ in
-                        self.isEditMode = false
-                        var meradatasSelect = [tableMetadata]()
-                        for ocId in self.selectOcId {
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                meradatasSelect.append(metadata)
-                            }
-                        }
-                        if meradatasSelect.count > 0 {
-                            NCFunctionCenter.shared.openSelectView(items: meradatasSelect, viewController: self)
-                        }
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
-                )
-            )
+            actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, completion: tapSelect))
 
             //
             // COPY
             //
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_copy_file_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
-                    action: { _ in
-                        self.isEditMode = false
-                        self.appDelegate.pasteboardOcIds.removeAll()
-                        for ocId in self.selectOcId {
-                            self.appDelegate.pasteboardOcIds.append(ocId)
-                        }
-                        NCFunctionCenter.shared.copyPasteboard()
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
-                )
-            )
+            actions.append(.copyAction(selectOcId: selectOcId, hudView: self.view, completion: tapSelect))
 
             //
             // DELETE
             //
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_delete_selected_files_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "trash"),
-                    action: { _ in
-                        self.isEditMode = false
-                        for ocId in self.selectOcId {
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false) { errorCode, errorDescription in
-                                    if errorCode != 0 {
-                                        NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
-                                    }
-                                }
-                            }
-                        }
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
-                    }
-                )
-            )
+            actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect))
         }
-
-        presentMenu(with: actions)
     }
 }

+ 0 - 30
iOSClient/Menu/NCMenu.swift

@@ -115,33 +115,3 @@ extension NCMenu: FloatingPanelControllerDelegate {
         fpc.dismiss(animated: true, completion: nil)
     }
 }
-
-class NCMenuAction {
-
-    let title: String
-    let icon: UIImage
-    let selectable: Bool
-    var onTitle: String?
-    var onIcon: UIImage?
-    var selected: Bool = false
-    var isOn: Bool = false
-    var action: ((_ menuAction: NCMenuAction) -> Void)?
-
-    init(title: String, icon: UIImage, action: ((_ menuAction: NCMenuAction) -> Void)?) {
-        self.title = title
-        self.icon = icon
-        self.action = action
-        self.selectable = false
-    }
-
-    init(title: String, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, action: ((_ menuAction: NCMenuAction) -> Void)?) {
-        self.title = title
-        self.icon = icon
-        self.onTitle = onTitle ?? title
-        self.onIcon = onIcon ?? icon
-        self.action = action
-        self.selected = selected
-        self.isOn = on
-        self.selectable = true
-    }
-}

+ 215 - 0
iOSClient/Menu/NCMenuAction.swift

@@ -0,0 +1,215 @@
+//
+//  NCMenuAction.swift
+//  Nextcloud
+//
+//  Created by Henrik Storch on 17.02.22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+class NCMenuAction {
+    let title: String
+    let icon: UIImage
+    let selectable: Bool
+    var onTitle: String?
+    var onIcon: UIImage?
+    var selected: Bool = false
+    var isOn: Bool = false
+    var action: ((_ menuAction: NCMenuAction) -> Void)?
+
+    init(title: String, icon: UIImage, action: ((_ menuAction: NCMenuAction) -> Void)?) {
+        self.title = title
+        self.icon = icon
+        self.action = action
+        self.selectable = false
+    }
+
+    init(title: String, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, action: ((_ menuAction: NCMenuAction) -> Void)?) {
+        self.title = title
+        self.icon = icon
+        self.onTitle = onTitle ?? title
+        self.onIcon = onIcon ?? icon
+        self.action = action
+        self.selected = selected
+        self.isOn = on
+        self.selectable = true
+    }
+}
+
+// MARK: - Actions
+
+extension NCMenuAction {
+
+    /// Select all items
+    static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction {
+        NCMenuAction(
+            title: NSLocalizedString("_select_all_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "checkmark.circle.fill"),
+            action: { _ in action() }
+        )
+    }
+
+    /// Copy files to pasteboard
+    static func copyAction(selectOcId: [String], hudView: UIView, completion: (() -> Void)? = nil) -> NCMenuAction {
+        NCMenuAction(
+            title: NSLocalizedString("_copy_file_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
+            action: { _ in
+                NCFunctionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId, hudView: hudView)
+                completion?()
+            }
+        )
+    }
+
+    /// Delete files either from cache or from Nextcloud
+    static func deleteAction(selectedMetadatas: [tableMetadata], metadataFolder: tableMetadata? = nil, viewController: UIViewController, completion: (() -> Void)? = nil) -> NCMenuAction {
+        var titleDelete = NSLocalizedString("_delete_", comment: "")
+        if selectedMetadatas.count > 1 {
+            titleDelete = NSLocalizedString("_delete_selected_files_", comment: "")
+        } else if let metadata = selectedMetadatas.first {
+            if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
+                titleDelete = NSLocalizedString("_leave_share_", comment: "")
+            } else if metadata.directory {
+                titleDelete = NSLocalizedString("_delete_folder_", comment: "")
+            } else {
+                titleDelete = NSLocalizedString("_delete_file_", comment: "")
+            }
+
+            if let metadataFolder = metadataFolder {
+                let isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
+                let isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
+                if isShare || isMounted {
+                    titleDelete = NSLocalizedString("_leave_share_", comment: "")
+                }
+            }
+        } // else: no metadata selected
+
+        var fileList = ""
+        for (ix, metadata) in selectedMetadatas.enumerated() {
+            guard ix < 3 else { fileList += "\n - ..."; break }
+            fileList += "\n - " + metadata.fileName
+        }
+
+        return NCMenuAction(
+            title: titleDelete,
+            icon: NCUtility.shared.loadImage(named: "trash"),
+            action: { _ in
+                let alertController = UIAlertController(
+                    title: titleDelete,
+                    message: NSLocalizedString("_want_delete_", comment: "") + fileList,
+                    preferredStyle: .alert)
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
+                    selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: false) })
+                    completion?()
+                })
+
+                // NCMedia removes image from collection view if removed from cache
+                if !(viewController is NCMedia) {
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
+                        selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: true) })
+                        completion?()
+                    })
+                }
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
+                viewController.present(alertController, animated: true, completion: nil)
+            }
+        )
+    }
+
+    /// Open "share view" (activity VC) to open files in another app
+    static func openInAction(selectedMetadatas: [tableMetadata], viewController: UIViewController, completion: (() -> Void)? = nil) -> NCMenuAction {
+        NCMenuAction(
+            title: NSLocalizedString("_open_in_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
+            action: { _ in
+                if viewController is NCFileViewInFolder {
+                    viewController.dismiss(animated: true) {
+                        NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
+                    }
+                } else {
+                    NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
+                }
+                completion?()
+            }
+        )
+    }
+
+    /// Save selected files to user's photo library
+    static func saveMediaAction(selectedMediaMetadatas: [tableMetadata], completion: (() -> Void)? = nil) -> NCMenuAction {
+        var title: String = NSLocalizedString("_save_selected_files_", comment: "")
+        var icon = NCUtility.shared.loadImage(named: "square.and.arrow.down")
+        if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) {
+            title = NSLocalizedString("_livephoto_save_", comment: "")
+            icon = NCUtility.shared.loadImage(named: "livephoto")
+        }
+
+        return NCMenuAction(
+            title: title,
+            icon: icon,
+            action: { _ in
+                for metadata in selectedMediaMetadatas {
+                    if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
+                        NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
+                    } else {
+                        if CCUtility.fileProviderStorageExists(metadata) {
+                            NCFunctionCenter.shared.saveAlbum(metadata: metadata)
+                        } else {
+                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
+                        }
+                    }
+                }
+                completion?()
+            }
+        )
+    }
+
+    /// Set (or remove) a file as *available offline*. Downloads the file if not downloaded already
+    static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, completion: (() -> Void)? = nil) -> NCMenuAction {
+        NCMenuAction(
+            title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
+            action: { _ in
+                if !isAnyOffline, selectedMetadatas.count > 3 {
+                    let alert = UIAlertController(
+                        title: NSLocalizedString("_set_available_offline_", comment: ""),
+                        message: NSLocalizedString("_select_offline_warning_", comment: ""),
+                        preferredStyle: .alert)
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
+                        selectedMetadatas.forEach { NCFunctionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+                        completion?()
+                    }))
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+                    viewController.present(alert, animated: true)
+                } else {
+                    selectedMetadatas.forEach { NCFunctionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+                    completion?()
+                }
+            }
+        )
+    }
+
+    /// Open view that lets the user move or copy the files within Nextcloud
+    static func moveOrCopyAction(selectedMetadatas: [tableMetadata], completion: (() -> Void)? = nil) -> NCMenuAction {
+        NCMenuAction(
+            title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
+            action: { _ in
+                NCFunctionCenter.shared.openSelectView(items: selectedMetadatas)
+                completion?()
+            }
+        )
+    }
+
+    /// Open AirPrint view to print a single file
+    static func printAction(metadata: tableMetadata) -> NCMenuAction {
+        NCMenuAction(
+            title: NSLocalizedString("_print_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "printer"),
+            action: { _ in
+                NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorPrint)
+            }
+        )
+    }
+}

+ 53 - 70
iOSClient/Menu/NCTrash+Menu.swift

@@ -4,8 +4,10 @@
 //
 //  Created by Marino Faggiana on 03/03/2021.
 //  Copyright © 2021 Marino Faggiana. All rights reserved.
+//  Copyright © 2022 Henrik Storch. All rights reserved.
 //
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -26,101 +28,82 @@ import FloatingPanel
 import NCCommunication
 
 extension NCTrash {
-
-    func toggleMenuMoreHeader() {
-
-        var actions: [NCMenuAction] = []
-
-        if isEditMode {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_trash_delete_selected_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "trash"),
-                    action: { _ in
-                        let alert = UIAlertController(title: NSLocalizedString("_trash_delete_selected_", comment: ""), message: "", preferredStyle: .alert)
-                        alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .destructive, handler: { _ in
-                            for ocId in self.selectOcId {
-                                self.deleteItem(with: ocId)
-                            }
-                            self.isEditMode = false
-                            self.selectOcId.removeAll()
-                            self.collectionView.reloadData()
-                        }))
-                        alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in
-                        }))
-                        self.present(alert, animated: true, completion: nil)
-                    }
-                )
-            )
-        } else {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_trash_delete_all_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "trash"),
-                    action: { _ in
-                        let alert = UIAlertController(title: NSLocalizedString("_trash_delete_all_", comment: ""), message: "", preferredStyle: .alert)
-                        alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .destructive, handler: { _ in
-                            self.emptyTrash()
-                        }))
-                        alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in
-                        }))
-                        self.present(alert, animated: true, completion: nil)
-                    }
-                )
+    var selectActions: [NCMenuAction] {
+        [
+            NCMenuAction(
+                title: NSLocalizedString("_trash_restore_selected_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "restore"),
+                action: { _ in
+                    self.selectOcId.forEach(self.restoreItem)
+                    self.tapSelect()
+                }
+            ),
+            NCMenuAction(
+                title: NSLocalizedString("_trash_delete_selected_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "trash"),
+                action: { _ in
+                    let alert = UIAlertController(title: NSLocalizedString("_trash_delete_selected_", comment: ""), message: "", preferredStyle: .alert)
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in
+                        self.selectOcId.forEach(self.deleteItem)
+                        self.tapSelect()
+                    }))
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in }))
+                    self.present(alert, animated: true, completion: nil)
+                }
             )
-        }
-
-        presentMenu(with: actions)
+        ]
     }
 
-    func toggleMenuMoreList(with objectId: String, image: UIImage?) {
+    func toggleMenuMoreHeader() {
 
         var actions: [NCMenuAction] = []
 
-        guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
-            return
-        }
-
-        var iconHeader: UIImage!
-        if let icon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(tableTrash.fileId, etag: tableTrash.fileName)) {
-            iconHeader = icon
-        } else {
-            if tableTrash.directory {
-                iconHeader = UIImage(named: "folder")!.image(color: NCBrandColor.shared.gray, size: 50)
-            } else {
-                iconHeader = UIImage(named: tableTrash.iconName)
-            }
-        }
-
         actions.append(
             NCMenuAction(
-                title: tableTrash.trashbinFileName,
-                icon: iconHeader,
-                action: nil
+                title: NSLocalizedString("_trash_restore_all_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "restore"),
+                action: { _ in
+                    self.datasource.forEach({ self.restoreItem(with: $0.fileId) })
+                }
             )
         )
 
         actions.append(
             NCMenuAction(
-                title: NSLocalizedString("_delete_", comment: ""),
+                title: NSLocalizedString("_trash_delete_all_", comment: ""),
                 icon: NCUtility.shared.loadImage(named: "trash"),
                 action: { _ in
-                    self.deleteItem(with: objectId)
+                    let alert = UIAlertController(title: NSLocalizedString("_trash_delete_all_description_", comment: ""), message: "", preferredStyle: .alert)
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_trash_delete_all_", comment: ""), style: .destructive, handler: { _ in
+                        self.emptyTrash()
+                    }))
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+                    self.present(alert, animated: true, completion: nil)
                 }
             )
         )
-
-        self.presentMenu(with: actions)
+        presentMenu(with: actions)
     }
 
-    func toggleMenuMoreGrid(with objectId: String, namedButtonMore: String, image: UIImage?) {
-
-        var actions: [NCMenuAction] = []
+    func toggleMenuMore(with objectId: String, image: UIImage?, isGridCell: Bool) {
 
         guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
             return
         }
 
+        guard isGridCell else {
+            let alert = UIAlertController(title: NSLocalizedString("_want_delete_", comment: ""), message: tableTrash.trashbinFileName, preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in
+                self.deleteItem(with: objectId)
+            }))
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+            self.present(alert, animated: true, completion: nil)
+
+            return
+        }
+
+        var actions: [NCMenuAction] = []
+
         var iconHeader: UIImage!
         if let icon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(tableTrash.fileId, etag: tableTrash.fileName)) {
             iconHeader = icon

+ 11 - 115
iOSClient/Menu/NCViewer+Menu.swift

@@ -29,29 +29,14 @@ extension NCViewer {
 
     func toggleMenu(viewController: UIViewController, metadata: tableMetadata, webView: Bool, imageIcon: UIImage?) {
 
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) else { return }
+        
         var actions = [NCMenuAction]()
-
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
         if metadata.favorite { titleFavorite = NSLocalizedString("_remove_favorites_", comment: "") }
         let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-
-        var titleOffline = ""
-        if localFile == nil || localFile!.offline == false {
-            titleOffline = NSLocalizedString("_set_available_offline_", comment: "")
-        } else {
-            titleOffline = NSLocalizedString("_remove_available_offline_", comment: "")
-        }
-
-        var titleDelete = NSLocalizedString("_delete_", comment: "")
-        if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: nil) {
-            titleDelete = NSLocalizedString("_leave_share_", comment: "")
-        } else if metadata.directory {
-            titleDelete = NSLocalizedString("_delete_folder_", comment: "")
-        } else {
-            titleDelete = NSLocalizedString("_delete_file_", comment: "")
-        }
-
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
+        let isOffline = localFile?.offline == true
 
         //
         // FAVORITE
@@ -89,34 +74,14 @@ extension NCViewer {
         // OFFLINE
         //
         if metadata.session == "" && !webView {
-            actions.append(
-                NCMenuAction(
-                    title: titleOffline,
-                    icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
-                    action: { _ in
-                        if (localFile == nil || !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView)) && metadata.session == "" {
-                            NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                        } else {
-                            NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: !localFile!.offline)
-                        }
-                    }
-                )
-            )
+            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: viewController))
         }
 
         //
         // OPEN IN
         //
         if metadata.session == "" && !webView {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_open_in_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
-                    action: { _ in
-                        NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorOpenIn)
-                    }
-                )
-            )
+            actions.append(.openInAction(selectedMetadatas: [metadata], viewController: viewController))
         }
 
         //
@@ -142,7 +107,7 @@ extension NCViewer {
             
             actions.append(
                 NCMenuAction(
-                    title: NSLocalizedString("_video_conversion_", comment: ""),
+                    title: NSLocalizedString("_video_processing_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "film"),
                     action: { menuAction in
                         if let ncplayer = (viewController as? NCViewerMediaPage)?.currentViewController.ncplayer {
@@ -158,28 +123,7 @@ extension NCViewer {
         // SAVE IMAGE / VIDEO
         //
         if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
-
-            var title: String = NSLocalizedString("_save_selected_files_", comment: "")
-            var icon = NCUtility.shared.loadImage(named: "square.and.arrow.down")
-            let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata)
-            if metadataMOV != nil {
-                title = NSLocalizedString("_livephoto_save_", comment: "")
-                icon = NCUtility.shared.loadImage(named: "livephoto")
-            }
-
-            actions.append(
-                NCMenuAction(
-                    title: title,
-                    icon: icon,
-                    action: { _ in
-                        if metadataMOV != nil {
-                            NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV!)
-                        } else {
-                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
-                        }
-                    }
-                )
-            )
+            actions.append(.saveMediaAction(selectedMediaMetadatas: [metadata]))
         }
 
         //
@@ -228,39 +172,13 @@ extension NCViewer {
         // COPY - MOVE
         //
         if !webView {
-            actions.append(
-                NCMenuAction(
-                    title: NSLocalizedString("_move_or_copy_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
-                    action: { _ in
-
-                        let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
-                        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
-                        let viewController = navigationController.topViewController as! NCSelect
-
-                        viewController.delegate = NCViewer.shared
-                        viewController.typeOfCommandView = .copyMove
-                        viewController.items = [metadata]
-
-                        self.appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
-                    }
-                )
-            )
+            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata]))
         }
 
         //
         // COPY
         //
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_copy_file_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
-                action: { _ in
-                    self.appDelegate.pasteboardOcIds = [metadata.ocId]
-                    NCFunctionCenter.shared.copyPasteboard()
-                }
-            )
-        )
+        actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: viewController.view))
 
         //
         // VIEW IN FOLDER
@@ -281,7 +199,7 @@ extension NCViewer {
         // DOWNLOAD IMAGE MAX RESOLUTION
         //
         if metadata.session == "" {
-            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) && metadata.session == "" {
+            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata) && metadata.session == "" {
                 actions.append(
                     NCMenuAction(
                         title: NSLocalizedString("_download_image_max_", comment: ""),
@@ -365,29 +283,7 @@ extension NCViewer {
         // DELETE
         //
         if !webView {
-            actions.append(
-                NCMenuAction(
-                    title: titleDelete,
-                    icon: NCUtility.shared.loadImage(named: "trash"),
-                    action: { _ in
-
-                        let alertController = UIAlertController(title: "", message: NSLocalizedString("_want_delete_", comment: ""), preferredStyle: .alert)
-
-                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-
-                            NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false) { errorCode, errorDescription in
-                                if errorCode != 0 {
-                                    NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
-                                }
-                            }
-                        })
-
-                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
-
-                        viewController.present(alertController, animated: true, completion: nil)
-                    }
-                )
-            )
+            actions.append(.deleteAction(selectedMetadatas: [metadata], metadataFolder: nil, viewController: viewController))
         }
 
         viewController.presentMenu(with: actions)

+ 5 - 4
iOSClient/Menu/UIViewController+Menu.swift

@@ -35,7 +35,7 @@ extension UIViewController {
                 url.scheme == "mailto",
                 let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
             else {
-                NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
+                NCContentPresenter.shared.showError(description: "_cannot_send_mail_error_")
                 return
             }
             sendEmail(to: components.path)
@@ -50,7 +50,7 @@ extension UIViewController {
 
         default:
             guard let url = action.hyperlinkUrl, UIApplication.shared.canOpenURL(url) else {
-                NCContentPresenter.shared.showGenericError(description: "_open_url_error_")
+                NCContentPresenter.shared.showError(description: "_open_url_error_")
                 return
             }
             UIApplication.shared.open(url, options: [:])
@@ -94,7 +94,7 @@ extension UIViewController {
 
     func sendEmail(to email: String) {
         guard MFMailComposeViewController.canSendMail() else {
-            NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
+            NCContentPresenter.shared.showError(description: "_cannot_send_mail_error_")
             return
         }
 
@@ -106,8 +106,9 @@ extension UIViewController {
     }
 
     func presentMenu(with actions: [NCMenuAction]) {
+        guard !actions.isEmpty else { return }
         guard let menuViewController = NCMenu.makeNCMenu(with: actions) else {
-            NCContentPresenter.shared.showGenericError(description: "_internal_generic_error_")
+            NCContentPresenter.shared.showError(description: "_internal_generic_error_")
             return
         }
 

+ 1 - 1
iOSClient/More/NCMore.swift

@@ -145,7 +145,7 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
             item = NCCommunicationExternalSite()
             item.name = "_scanned_images_"
             item.icon = "doc.text.viewfinder"
-            item.url = "openStoryboardScan"
+            item.url = "openStoryboardNCScan"
             functionMenu.append(item)
         }
 

+ 1 - 2
iOSClient/NCGlobal.swift

@@ -112,7 +112,7 @@ class NCGlobal: NSObject {
     // Database Realm
     //
     let databaseDefault                             = "nextcloud.realm"
-    let databaseSchemaVersion: UInt64               = 215
+    let databaseSchemaVersion: UInt64               = 216
 
     // Intro selector
     //
@@ -272,7 +272,6 @@ class NCGlobal: NSObject {
     let selectorListingFavorite                     = "listingFavorite"
     let selectorLoadFileView                        = "loadFileView"
     let selectorLoadFileQuickLook                   = "loadFileQuickLook"
-    let selectorLoadCopy                            = "loadCopy"
     let selectorLoadOffline                         = "loadOffline"
     let selectorOpenIn                              = "openIn"
     let selectorPrint                               = "print"

+ 8 - 5
iOSClient/Networking/NCNetworking.swift

@@ -327,7 +327,7 @@ import Queuer
         }
     }
     
-    @objc func download(metadata: tableMetadata, selector: String, notificationCenterProgressTask: Bool = true, progressHandler: @escaping (_ progress: Progress) -> () = { _ in }, completion: @escaping (_ errorCode: Int)->()) {
+    @objc func download(metadata: tableMetadata, selector: String, notificationCenterProgressTask: Bool = true, progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }, completion: @escaping (_ errorCode: Int) -> Void) {
         
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
@@ -363,7 +363,7 @@ import Queuer
             
             progressHandler(progress)
                                         
-        }) { (account, etag, date, length, allHeaderFields, error, errorCode, errorDescription) in
+        }) { (account, etag, date, _, allHeaderFields, error, errorCode, errorDescription) in
               
             if error?.isExplicitlyCancelledError ?? false {
 
@@ -448,7 +448,7 @@ import Queuer
         
         let metadata = tableMetadata.init(value: metadata)
 
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+        if CCUtility.fileProviderStorageExists(metadata) {
 
             let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
             let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false)
@@ -929,7 +929,9 @@ import Queuer
     @objc func createFolder(fileName: String, serverUrl: String, account: String, urlBase: String, overwrite: Bool = false, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> Void) {
 
         let isDirectoryEncrypted = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: account, urlBase: urlBase)
-
+        
+        let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
+        
         if isDirectoryEncrypted {
             #if !EXTENSION
             NCNetworkingE2EE.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: account, urlBase: urlBase, completion: completion)
@@ -1181,6 +1183,7 @@ import Queuer
 
         let isDirectoryEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         let metadataLive = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata)
+        let fileNameNew = fileNameNew.trimmingCharacters(in: .whitespacesAndNewlines)
         let fileNameNewLive = (fileNameNew as NSString).deletingPathExtension + ".mov"
 
         if isDirectoryEncrypted {
@@ -1364,7 +1367,7 @@ import Queuer
 
     func getVideoUrl(metadata: tableMetadata, completition: @escaping (_ url: URL?) -> Void) {
 
-        if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+        if CCUtility.fileProviderStorageExists(metadata) {
 
             completition(URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)))
 

+ 3 - 3
iOSClient/Rename file/NCRenameFile.swift

@@ -70,7 +70,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             fileNameWithoutExt.delegate = self
             fileNameWithoutExt.becomeFirstResponder()
 
-            ext.text = (metadata.fileNameView as NSString).pathExtension
+            ext.text = metadata.fileExtension
             ext.delegate = self
             if disableChangeExt {
                 ext.isEnabled = false
@@ -176,7 +176,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             } else {
 
                 if ext.text == nil || ext.text?.count == 0 {
-                    self.ext.text = (metadata.fileNameView as NSString).pathExtension
+                    self.ext.text = metadata.fileExtension
                     return
                 } else {
                     extNew = ext.text!
@@ -196,7 +196,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
 
                     title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext
                     alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
-                        self.ext.text = (metadata.fileNameView as NSString).pathExtension
+                        self.ext.text = metadata.fileExtension
                     }))
 
                     self.present(alertController, animated: true)

+ 226 - 0
iOSClient/ScanDocument/NCScan+CollectionView.swift

@@ -0,0 +1,226 @@
+//
+//  NCScan+CollectionView.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 22/02/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+
+@available(iOS 13.0, *)
+extension NCScan: UICollectionViewDataSource {
+
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+
+        return collectionView == collectionViewSource ? itemsSource.count : imagesDestination.count
+    }
+
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+
+        if collectionView == collectionViewSource {
+
+            let cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as? NCScanCell)!
+
+            let fileNamePath = CCUtility.getDirectoryScan() + "/" + itemsSource[indexPath.row]
+
+            guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePath)), var image = UIImage(data: data) else { return cell }
+
+            let imageWidthInPixels = image.size.width * image.scale
+            let imageHeightInPixels = image.size.height * image.scale
+
+            // 72 DPI
+            if imageWidthInPixels > 595 || imageHeightInPixels > 842 {
+                image = image.resizeImage(size: CGSize(width: 595, height: 842), isAspectRation: true) ?? image
+            }
+
+            cell.customImageView?.image = image
+            cell.delete.action(for: .touchUpInside) { sender in
+
+                let buttonPosition: CGPoint = (sender as? UIButton)!.convert(.zero, to: self.collectionViewSource)
+                if let indexPath = self.collectionViewSource.indexPathForItem(at: buttonPosition) {
+
+                    let fileNameAtPath = CCUtility.getDirectoryScan() + "/" + self.itemsSource[indexPath.row]
+                    CCUtility.removeFile(atPath: fileNameAtPath)
+                    self.itemsSource.remove(at: indexPath.row)
+
+                    self.collectionViewSource.deleteItems(at: [indexPath])
+                }
+            }
+
+            return cell
+
+        } else {
+
+            let cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath) as? NCScanCell)!
+            cell.delegate = self
+            cell.index = indexPath.row
+
+            var image = imagesDestination[indexPath.row]
+
+            let imageWidthInPixels = image.size.width * image.scale
+            let imageHeightInPixels = image.size.height * image.scale
+
+            // 72 DPI
+            if imageWidthInPixels > 595 || imageHeightInPixels > 842 {
+                image = image.resizeImage(size: CGSize(width: 595, height: 842), isAspectRation: true) ?? image
+            }
+
+            cell.customImageView?.image = filter(image: image)
+            cell.customLabel.text = NSLocalizedString("_scan_document_pdf_page_", comment: "") + " " + "\(indexPath.row + 1)"
+
+            return cell
+        }
+    }
+}
+
+@available(iOS 13.0, *)
+extension NCScan: UICollectionViewDragDelegate {
+    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
+
+        if collectionView == collectionViewSource {
+            let item = itemsSource[indexPath.row]
+            let itemProvider = NSItemProvider(object: item as NSString)
+            let dragItem = UIDragItem(itemProvider: itemProvider)
+
+            dragItem.localObject = item
+
+            return [dragItem]
+
+        } else {
+            let item = imagesDestination[indexPath.row]
+            let itemProvider = NSItemProvider(object: item as UIImage)
+            let dragItem = UIDragItem(itemProvider: itemProvider)
+
+            dragItem.localObject = item
+
+            return [dragItem]
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
+
+        if collectionView == collectionViewSource {
+            let item = itemsSource[indexPath.row]
+            let itemProvider = NSItemProvider(object: item as NSString)
+            let dragItem = UIDragItem(itemProvider: itemProvider)
+
+            dragItem.localObject = item
+
+            return [dragItem]
+
+        } else {
+            let item = imagesDestination[indexPath.row]
+            let itemProvider = NSItemProvider(object: item as UIImage)
+            let dragItem = UIDragItem(itemProvider: itemProvider)
+
+            dragItem.localObject = item
+
+            return [dragItem]
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
+
+        let previewParameters = UIDragPreviewParameters()
+        if collectionView == collectionViewSource {
+            previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 100, height: 100))
+        } else {
+            previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 80, height: 80))
+        }
+
+        return previewParameters
+    }
+}
+
+@available(iOS 13.0, *)
+extension NCScan: UICollectionViewDropDelegate {
+
+    func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
+
+        return true // session.canLoadObjects(ofClass: NSString.self)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
+
+        if collectionView == collectionViewSource {
+
+            if collectionView.hasActiveDrag {
+                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
+            } else {
+                return UICollectionViewDropProposal(operation: .forbidden)
+            }
+
+        } else {
+
+            if collectionView.hasActiveDrag {
+                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
+            } else {
+                return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
+            }
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
+
+        let destinationIndexPath: IndexPath
+
+        switch coordinator.proposal.operation {
+
+        case .move:
+
+            if let indexPath = coordinator.destinationIndexPath {
+
+                destinationIndexPath = indexPath
+
+            } else {
+
+                // Get last index path of table view.
+                let section = collectionView.numberOfSections - 1
+                let row = collectionView.numberOfItems(inSection: section)
+
+                destinationIndexPath = IndexPath(row: row, section: section)
+            }
+            reorderItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
+
+        case .copy:
+
+            // Get last index path of table view.
+            let section = collectionView.numberOfSections - 1
+            let row = collectionView.numberOfItems(inSection: section)
+
+            destinationIndexPath = IndexPath(row: row, section: section)
+            copyItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
+
+        default:
+            return
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession) {
+
+        collectionViewDestination.reloadData()
+
+        // Save button
+        if imagesDestination.isEmpty {
+            save.isEnabled = false
+        } else {
+            save.isEnabled = true
+        }
+    }
+}

+ 20 - 17
iOSClient/ScanDocument/Scan.storyboard → iOSClient/ScanDocument/NCScan.storyboard

@@ -1,23 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bdK-eL-mz4">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bdK-eL-mz4">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
-        <!--Drag Drop View Controller-->
+        <!--Scan-->
         <scene sceneID="tne-QT-ifu">
             <objects>
-                <viewController extendedLayoutIncludesOpaqueBars="YES" id="BYZ-38-t0r" customClass="DragDropViewController" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController extendedLayoutIncludesOpaqueBars="YES" id="BYZ-38-t0r" customClass="NCScan" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="jwq-kF-6Nq" userLabel="collectionViewSource">
-                                <rect key="frame" x="57" y="52" width="318" height="160"/>
+                                <rect key="frame" x="57" y="44" width="318" height="160"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="160" id="0Wb-eO-Qiu"/>
@@ -29,7 +29,7 @@
                                     <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                                 </collectionViewFlowLayout>
                                 <cells>
-                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell1" id="Lca-vD-NY2" customClass="ScanCell" customModule="Nextcloud" customModuleProvider="target">
+                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell1" id="Lca-vD-NY2" customClass="NCScanCell" customModule="Nextcloud" customModuleProvider="target">
                                         <rect key="frame" x="0.0" y="10" width="140" height="140"/>
                                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
@@ -69,11 +69,8 @@
                                 </connections>
                             </collectionView>
                             <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="fGo-qU-AYi" userLabel="collectionViewDestination">
-                                <rect key="frame" x="0.0" y="258" width="375" height="359"/>
+                                <rect key="frame" x="0.0" y="239" width="375" height="378"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                <constraints>
-                                    <constraint firstAttribute="height" constant="339" id="nTl-vy-iQ8"/>
-                                </constraints>
                                 <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="9Sn-Y3-S86">
                                     <size key="itemSize" width="120" height="120"/>
                                     <size key="headerReferenceSize" width="0.0" height="0.0"/>
@@ -81,7 +78,7 @@
                                     <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                                 </collectionViewFlowLayout>
                                 <cells>
-                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell2" id="Pph-tY-PGX" customClass="ScanCell" customModule="Nextcloud" customModuleProvider="target">
+                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell2" id="Pph-tY-PGX" customClass="NCScanCell" customModule="Nextcloud" customModuleProvider="target">
                                         <rect key="frame" x="0.0" y="0.0" width="120" height="120"/>
                                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
@@ -108,6 +105,9 @@
                                                         <constraint firstAttribute="height" constant="23" id="QOj-Nj-nAA"/>
                                                     </constraints>
                                                     <state key="normal" image="deleteScan"/>
+                                                    <connections>
+                                                        <action selector="touchUpInsideDelete:" destination="Pph-tY-PGX" eventType="touchUpInside" id="KcT-WM-s1K"/>
+                                                    </connections>
                                                 </button>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XEo-o0-dSF" userLabel="Rotate">
                                                     <rect key="frame" x="98" y="0.0" width="22" height="22"/>
@@ -116,6 +116,9 @@
                                                         <constraint firstAttribute="height" constant="22" id="fd5-QY-wlr"/>
                                                     </constraints>
                                                     <state key="normal" image="rotate"/>
+                                                    <connections>
+                                                        <action selector="touchUpInsideRotate:" destination="Pph-tY-PGX" eventType="touchUpInside" id="x5z-go-m4Y"/>
+                                                    </connections>
                                                 </button>
                                             </subviews>
                                         </view>
@@ -146,7 +149,7 @@
                                 </connections>
                             </collectionView>
                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PDF" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YHy-9G-ngy">
-                                <rect key="frame" x="0.0" y="220" width="375" height="30"/>
+                                <rect key="frame" x="0.0" y="204" width="375" height="30"/>
                                 <color key="backgroundColor" white="1" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="30" id="vrg-ki-2Lk"/>
@@ -156,7 +159,7 @@
                                 <nil key="highlightedColor"/>
                             </label>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0Gy-eG-A0f">
-                                <rect key="frame" x="20" y="82" width="30" height="30"/>
+                                <rect key="frame" x="20" y="74" width="30" height="30"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="30" id="A6e-zC-q5t"/>
                                     <constraint firstAttribute="width" constant="30" id="U2p-ow-Iad"/>
@@ -181,7 +184,7 @@
                                 </connections>
                             </segmentedControl>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fqr-e6-s3n" userLabel="transferDown">
-                                <rect key="frame" x="20" y="152" width="30" height="30"/>
+                                <rect key="frame" x="20" y="144" width="30" height="30"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="30" id="YPl-DJ-aNS"/>
                                     <constraint firstAttribute="width" constant="30" id="yGb-Y2-8Oj"/>
@@ -199,7 +202,7 @@
                             <constraint firstItem="Fqr-e6-s3n" firstAttribute="top" secondItem="jwq-kF-6Nq" secondAttribute="bottom" constant="-60" id="5FJ-rD-ncI"/>
                             <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="jwq-kF-6Nq" secondAttribute="trailing" id="6EM-YK-lEy"/>
                             <constraint firstItem="jwq-kF-6Nq" firstAttribute="top" secondItem="0Gy-eG-A0f" secondAttribute="bottom" constant="-60" id="8D3-HY-PIM"/>
-                            <constraint firstItem="YHy-9G-ngy" firstAttribute="top" secondItem="jwq-kF-6Nq" secondAttribute="bottom" constant="8" id="90f-NC-udY"/>
+                            <constraint firstItem="YHy-9G-ngy" firstAttribute="top" secondItem="jwq-kF-6Nq" secondAttribute="bottom" id="90f-NC-udY"/>
                             <constraint firstItem="0Gy-eG-A0f" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="BNZ-HM-6yi"/>
                             <constraint firstItem="jwq-kF-6Nq" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="57" id="ELU-RG-o16"/>
                             <constraint firstItem="fGo-qU-AYi" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="Ew2-9q-DKl"/>
@@ -208,11 +211,11 @@
                             <constraint firstAttribute="trailingMargin" secondItem="b3i-bF-ITj" secondAttribute="trailing" constant="8" id="Ogg-xT-qyc"/>
                             <constraint firstItem="Fqr-e6-s3n" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="UJ4-nd-oV8"/>
                             <constraint firstItem="fGo-qU-AYi" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="aU0-Bn-B6T"/>
-                            <constraint firstItem="jwq-kF-6Nq" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="8" id="dyH-ym-lrb"/>
+                            <constraint firstItem="jwq-kF-6Nq" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="dyH-ym-lrb"/>
                             <constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="b3i-bF-ITj" secondAttribute="bottom" constant="11" id="iCt-vj-sDA"/>
                             <constraint firstItem="b3i-bF-ITj" firstAttribute="top" secondItem="fGo-qU-AYi" secondAttribute="bottom" constant="12" id="kGQ-oN-ccm"/>
                             <constraint firstItem="b3i-bF-ITj" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="p4i-a8-soi"/>
-                            <constraint firstItem="fGo-qU-AYi" firstAttribute="top" secondItem="YHy-9G-ngy" secondAttribute="bottom" constant="8" id="x2i-aj-hzf"/>
+                            <constraint firstItem="fGo-qU-AYi" firstAttribute="top" secondItem="YHy-9G-ngy" secondAttribute="bottom" constant="5" id="x2i-aj-hzf"/>
                         </constraints>
                     </view>
                     <navigationItem key="navigationItem" id="zeb-Ex-mE8">

+ 372 - 0
iOSClient/ScanDocument/NCScan.swift

@@ -0,0 +1,372 @@
+//
+//  NCScan.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 21/08/18.
+//  Copyright (c) 2018 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+
+@available(iOS 13.0, *)
+class NCScan: UIViewController, NCScanCellCellDelegate {
+
+    @IBOutlet weak var collectionViewSource: UICollectionView!
+    @IBOutlet weak var collectionViewDestination: UICollectionView!
+    @IBOutlet weak var cancel: UIBarButtonItem!
+    @IBOutlet weak var save: UIBarButtonItem!
+    @IBOutlet weak var add: UIButton!
+    @IBOutlet weak var transferDown: UIButton!
+    @IBOutlet weak var labelTitlePDFzone: UILabel!
+    @IBOutlet weak var segmentControlFilter: UISegmentedControl!
+
+    // Data Source for collectionViewSource
+    internal var itemsSource: [String] = []
+
+    // Data Source for collectionViewDestination
+    internal var imagesDestination: [UIImage] = []
+    internal var itemsDestination: [String] = []
+
+    internal let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+
+    enum TypeFilter {
+        case original
+        case grayScale
+        case bn
+    }
+    internal var filter: TypeFilter = TypeFilter.original
+
+    // MARK: - View Life Cycle
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        view.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+        navigationItem.title = NSLocalizedString("_scanned_images_", comment: "")
+
+        collectionViewSource.dragInteractionEnabled = true
+        collectionViewSource.dragDelegate = self
+        collectionViewSource.dropDelegate = self
+        collectionViewSource.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
+        collectionViewDestination.dragInteractionEnabled = true
+        collectionViewDestination.dropDelegate = self
+        collectionViewDestination.dragDelegate = self
+        collectionViewDestination.reorderingCadence = .fast // default value - .immediate
+        collectionViewDestination.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
+        cancel.title = NSLocalizedString("_cancel_", comment: "")
+        save.title = NSLocalizedString("_save_", comment: "")
+
+        labelTitlePDFzone.text = NSLocalizedString("_scan_label_document_zone_", comment: "")
+        labelTitlePDFzone.backgroundColor = NCBrandColor.shared.systemGray6
+        labelTitlePDFzone.textColor = NCBrandColor.shared.label
+
+        segmentControlFilter.setTitle(NSLocalizedString("_filter_original_", comment: ""), forSegmentAt: 0)
+        segmentControlFilter.setTitle(NSLocalizedString("_filter_grayscale_", comment: ""), forSegmentAt: 1)
+        segmentControlFilter.setTitle(NSLocalizedString("_filter_bn_", comment: ""), forSegmentAt: 2)
+
+        add.setImage(UIImage(named: "plus")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
+        transferDown.setImage(UIImage(named: "transferDown")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
+
+        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(recognizer:)))
+        collectionViewSource.addGestureRecognizer(longPressRecognizer)
+        let longPressRecognizerPlus = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(recognizer:)))
+        add.addGestureRecognizer(longPressRecognizerPlus)
+
+        collectionViewSource.reloadData()
+        collectionViewDestination.reloadData()
+
+        loadImage()
+    }
+
+    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+        super.traitCollectionDidChange(previousTraitCollection)
+
+        add.setImage(UIImage(named: "plus")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
+        transferDown.setImage(UIImage(named: "transferDown")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
+    }
+
+    override var canBecomeFirstResponder: Bool { return true }
+
+    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
+        if action == #selector(pasteImage) {
+            return true
+        }
+        return false
+    }
+
+    @IBAction func cancelAction(sender: UIBarButtonItem) {
+        self.dismiss(animated: true, completion: nil)
+    }
+
+    @IBAction func saveAction(sender: UIBarButtonItem) {
+
+        if !imagesDestination.isEmpty {
+
+            var images: [UIImage] = []
+            let serverUrl = appDelegate.activeServerUrl
+
+            for image in imagesDestination {
+                images.append(filter(image: image)!)
+            }
+
+            let formViewController = NCCreateFormUploadScanDocument(serverUrl: serverUrl, arrayImages: images)
+            self.navigationController?.pushViewController(formViewController, animated: true)
+        }
+    }
+
+    @IBAction func add(sender: UIButton) {
+
+        NCCreateScanDocument.shared.openScannerDocument(viewController: self)
+    }
+
+    @IBAction func transferDown(sender: UIButton) {
+
+        for fileName in itemsSource {
+
+            if !itemsDestination.contains(fileName) {
+
+                let fileNamePathAt = CCUtility.getDirectoryScan() + "/" + fileName
+
+                guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePathAt)), let image = UIImage(data: data) else { return }
+
+                imagesDestination.append(image)
+                itemsDestination.append(fileName)
+            }
+        }
+
+        // Save button
+        if imagesDestination.isEmpty {
+            save.isEnabled = false
+        } else {
+            save.isEnabled = true
+        }
+
+        collectionViewDestination.reloadData()
+    }
+
+    @IBAction func indexChanged(_ sender: AnyObject) {
+
+        switch segmentControlFilter.selectedSegmentIndex {
+        case 0:
+            filter = .original
+        case 1:
+            filter = .grayScale
+        case 2:
+            filter = .bn
+        default:
+            break
+        }
+
+        collectionViewDestination.reloadData()
+    }
+
+    func loadImage() {
+
+        itemsSource.removeAll()
+
+        do {
+            let atPath = CCUtility.getDirectoryScan()!
+            let directoryContents = try FileManager.default.contentsOfDirectory(atPath: atPath)
+            for fileName in directoryContents where fileName.first != "." {
+                itemsSource.append(fileName)
+            }
+        } catch {
+            print(error.localizedDescription)
+        }
+
+        itemsSource = itemsSource.sorted()
+
+        collectionViewSource.reloadData()
+
+        // Save button
+        if imagesDestination.isEmpty {
+            save.isEnabled = false
+        } else {
+            save.isEnabled = true
+        }
+    }
+
+    func filter(image: UIImage) -> UIImage? {
+
+        var inputContrast: Double = 0
+
+        if filter == .original {
+            return image
+        }
+
+        if filter == .grayScale {
+            inputContrast = 1
+        }
+
+        if filter == .bn {
+            inputContrast = 4
+        }
+
+        let ciImage = CIImage(image: image)!
+        let imageFilter = ciImage.applyingFilter("CIColorControls", parameters: ["inputSaturation": 0, "inputContrast": inputContrast])
+
+        let context: CIContext = CIContext(options: nil)
+        let cgImage: CGImage = context.createCGImage(imageFilter, from: imageFilter.extent)!
+        let image: UIImage = UIImage(cgImage: cgImage)
+        return image
+    }
+
+    // destinationIndexPath: indexpath of the collection view where the user drops the element
+    // collectionView: collectionView in which reordering needs to be done.
+
+    func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
+
+        let items = coordinator.items
+
+        if items.count == 1, let item = items.first, let sourceIndexPath = item.sourceIndexPath {
+
+            var dIndexPath = destinationIndexPath
+
+            if dIndexPath.row >= collectionView.numberOfItems(inSection: 0) {
+                dIndexPath.row = collectionView.numberOfItems(inSection: 0) - 1
+            }
+
+            collectionView.performBatchUpdates({
+
+                if collectionView === collectionViewDestination {
+
+                    imagesDestination.remove(at: sourceIndexPath.row)
+                    imagesDestination.insert((item.dragItem.localObject as? UIImage)!, at: dIndexPath.row)
+
+                    let fileName = itemsDestination[sourceIndexPath.row]
+                    itemsDestination.remove(at: sourceIndexPath.row)
+                    itemsDestination.insert(fileName, at: dIndexPath.row)
+
+                } else {
+
+                    itemsSource.remove(at: sourceIndexPath.row)
+                    itemsSource.insert((item.dragItem.localObject as? String)!, at: dIndexPath.row)
+                }
+
+                collectionView.deleteItems(at: [sourceIndexPath])
+                collectionView.insertItems(at: [dIndexPath])
+            })
+
+            coordinator.drop(items.first!.dragItem, toItemAt: dIndexPath)
+        }
+    }
+
+    func copyItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
+        collectionView.performBatchUpdates({
+
+            var indexPaths: [IndexPath] = []
+
+            for (index, item) in coordinator.items.enumerated() {
+
+                let indexPath = IndexPath(row: destinationIndexPath.row + index, section: destinationIndexPath.section)
+
+                if collectionView === collectionViewDestination {
+
+                    let fileName = (item.dragItem.localObject as? String)!
+                    let fileNamePathAt = CCUtility.getDirectoryScan() + "/" + fileName
+
+                    guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePathAt)), let image = UIImage(data: data) else { return }
+
+                    imagesDestination.insert(image, at: indexPath.row)
+                    itemsDestination.insert(fileName, at: indexPath.row)
+
+                } else {
+
+                    // NOT PERMITTED
+                    return
+                }
+
+                indexPaths.append(indexPath)
+            }
+
+            collectionView.insertItems(at: indexPaths)
+        })
+    }
+
+    @objc func handleLongPressGesture(recognizer: UIGestureRecognizer) {
+
+        if recognizer.state == UIGestureRecognizer.State.began {
+
+            self.becomeFirstResponder()
+
+            let pasteboard = UIPasteboard.general
+
+            if let recognizerView = recognizer.view, let recognizerSuperView = recognizerView.superview, pasteboard.hasImages {
+
+                UIMenuController.shared.menuItems = [UIMenuItem(title: "Paste", action: #selector(pasteImage))]
+                UIMenuController.shared.setTargetRect(recognizerView.frame, in: recognizerSuperView)
+                UIMenuController.shared.setMenuVisible(true, animated: true)
+            }
+        }
+    }
+
+    @objc func pasteImage() {
+
+        let pasteboard = UIPasteboard.general
+
+        if pasteboard.hasImages {
+
+            guard let image = pasteboard.image?.fixedOrientation() else { return }
+
+            let fileName = CCUtility.createFileName("scan.png", fileDate: Date(),
+                                                    fileType: PHAssetMediaType.image,
+                                                    keyFileName: NCGlobal.shared.keyFileNameMask,
+                                                    keyFileNameType: NCGlobal.shared.keyFileNameType,
+                                                    keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal,
+                                                    forcedNewFileName: true)!
+            let fileNamePath = CCUtility.getDirectoryScan() + "/" + fileName
+
+            do {
+                try image.pngData()?.write(to: NSURL.fileURL(withPath: fileNamePath), options: .atomic)
+            } catch {
+                return
+            }
+
+            loadImage()
+        }
+    }
+
+    func delete(with imageIndex: Int, sender: Any) {
+
+        imagesDestination.remove(at: imageIndex)
+        itemsDestination.remove(at: imageIndex)
+
+        // Save button
+        if imagesDestination.isEmpty {
+            save.isEnabled = false
+        } else {
+            save.isEnabled = true
+        }
+
+        collectionViewDestination.reloadData()
+    }
+
+    func rotate(with imageIndex: Int, sender: Any) {
+
+        let indexPath = IndexPath(row: imageIndex, section: 0)
+        if let cell = collectionViewDestination.cellForItem(at: indexPath) as? NCScanCell {
+
+            var image = imagesDestination[imageIndex]
+            image = image.rotate(radians: .pi / 2)!
+            imagesDestination[imageIndex] = image
+            cell.customImageView.image = image
+        }
+    }
+}

+ 18 - 2
iOSClient/ScanDocument/ScanCell.swift → iOSClient/ScanDocument/NCScanCell.swift

@@ -1,5 +1,5 @@
 //
-//  ScanCell.swift
+//  NCScanCell.swift
 //  Nextcloud
 //
 //  Created by Marino Faggiana on 21/08/18.
@@ -23,10 +23,26 @@
 
 import UIKit
 
-class ScanCell: UICollectionViewCell {
+class NCScanCell: UICollectionViewCell {
 
     @IBOutlet weak var customImageView: UIImageView!
     @IBOutlet weak var customLabel: UILabel!
     @IBOutlet weak var delete: UIButton!
     @IBOutlet weak var rotate: UIButton!
+
+    weak var delegate: NCScanCellCellDelegate?
+    var index = 0
+
+    @IBAction func touchUpInsideDelete(_ sender: Any) {
+        delegate?.delete(with: index, sender: sender)
+    }
+
+    @IBAction func touchUpInsideRotate(_ sender: Any) {
+        delegate?.rotate(with: index, sender: sender)
+    }
+}
+
+protocol NCScanCellCellDelegate: AnyObject {
+    func delete(with index: Int, sender: Any)
+    func rotate(with index: Int, sender: Any)
 }

+ 0 - 642
iOSClient/ScanDocument/ScanCollectionView.swift

@@ -1,642 +0,0 @@
-//
-//  ScanCollectionView.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 21/08/18.
-//  Copyright (c) 2018 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import UIKit
-
-@available(iOS 13.0, *)
-class DragDropViewController: UIViewController {
-
-    // Data Source for collectionViewSource
-    private var itemsSource: [String] = []
-
-    // Data Source for collectionViewDestination
-    private var imagesDestination: [UIImage] = []
-    private var itemsDestination: [String] = []
-
-    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
-
-    // MARK: Outlets
-    @IBOutlet weak var collectionViewSource: UICollectionView!
-    @IBOutlet weak var collectionViewDestination: UICollectionView!
-    @IBOutlet weak var cancel: UIBarButtonItem!
-    @IBOutlet weak var save: UIBarButtonItem!
-    @IBOutlet weak var add: UIButton!
-    @IBOutlet weak var transferDown: UIButton!
-    @IBOutlet weak var labelTitlePDFzone: UILabel!
-    @IBOutlet weak var segmentControlFilter: UISegmentedControl!
-
-    // filter
-    enum typeFilter {
-        case original
-        case grayScale
-        case bn
-    }
-    private var filter: typeFilter = typeFilter.original
-
-    override var canBecomeFirstResponder: Bool { return true }
-
-    // MARK: - View Life Cycle
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        view.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-        navigationItem.title = NSLocalizedString("_scanned_images_", comment: "")
-
-        collectionViewSource.dragInteractionEnabled = true
-        collectionViewSource.dragDelegate = self
-        collectionViewSource.dropDelegate = self
-        collectionViewSource.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        collectionViewDestination.dragInteractionEnabled = true
-        collectionViewDestination.dropDelegate = self
-        collectionViewDestination.dragDelegate = self
-        collectionViewDestination.reorderingCadence = .fast // default value - .immediate
-        collectionViewDestination.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        cancel.title = NSLocalizedString("_cancel_", comment: "")
-        save.title = NSLocalizedString("_save_", comment: "")
-
-        labelTitlePDFzone.text = NSLocalizedString("_scan_label_document_zone_", comment: "")
-        labelTitlePDFzone.backgroundColor = NCBrandColor.shared.systemGray6
-        labelTitlePDFzone.textColor = NCBrandColor.shared.label
-
-        segmentControlFilter.setTitle(NSLocalizedString("_filter_original_", comment: ""), forSegmentAt: 0)
-        segmentControlFilter.setTitle(NSLocalizedString("_filter_grayscale_", comment: ""), forSegmentAt: 1)
-        segmentControlFilter.setTitle(NSLocalizedString("_filter_bn_", comment: ""), forSegmentAt: 2)
-
-        add.setImage(UIImage(named: "plus")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
-        transferDown.setImage(UIImage(named: "transferDown")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
-
-        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(recognizer:)))
-        collectionViewSource.addGestureRecognizer(longPressRecognizer)
-        let longPressRecognizerPlus = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(recognizer:)))
-        add.addGestureRecognizer(longPressRecognizerPlus)
-
-        collectionViewSource.reloadData()
-        collectionViewDestination.reloadData()
-
-        loadImage()
-    }
-
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        add.setImage(UIImage(named: "plus")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
-        transferDown.setImage(UIImage(named: "transferDown")?.image(color: NCBrandColor.shared.label, size: 25), for: .normal)
-    }
-
-    // MARK: Button Action
-
-    @IBAction func cancelAction(sender: UIBarButtonItem) {
-        self.dismiss(animated: true, completion: nil)
-    }
-
-    @IBAction func saveAction(sender: UIBarButtonItem) {
-
-        if imagesDestination.count > 0 {
-
-            var images: [UIImage] = []
-            let serverUrl = appDelegate.activeServerUrl
-
-            for image in imagesDestination {
-                images.append(filter(image: image)!)
-            }
-
-//            if let directory = CCUtility.getDirectoryScanDocuments() {
-//                serverUrl = directory
-//            }
-
-            let formViewController = NCCreateFormUploadScanDocument(serverUrl: serverUrl, arrayImages: images)
-            self.navigationController?.pushViewController(formViewController, animated: true)
-        }
-    }
-
-    @IBAction func add(sender: UIButton) {
-
-        NCCreateScanDocument.shared.openScannerDocument(viewController: self)
-    }
-
-    @IBAction func transferDown(sender: UIButton) {
-
-        for fileName in itemsSource {
-
-            if !itemsDestination.contains(fileName) {
-
-                let fileNamePathAt = CCUtility.getDirectoryScan() + "/" + fileName
-
-                guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePathAt)) else { return }
-                guard let image = UIImage(data: data) else { return }
-
-                imagesDestination.append(image)
-                itemsDestination.append(fileName)
-            }
-        }
-
-        // Save button
-        if imagesDestination.count == 0 {
-            save.isEnabled = false
-        } else {
-            save.isEnabled = true
-        }
-
-        collectionViewDestination.reloadData()
-    }
-
-    @IBAction func indexChanged(_ sender: AnyObject) {
-
-        switch segmentControlFilter.selectedSegmentIndex {
-        case 0:
-            filter = typeFilter.original
-        case 1:
-            filter = typeFilter.grayScale
-        case 2:
-            filter = typeFilter.bn
-        default:
-            break
-        }
-
-        collectionViewDestination.reloadData()
-    }
-
-    func loadImage() {
-
-        itemsSource.removeAll()
-
-        do {
-            let atPath = CCUtility.getDirectoryScan()!
-            let directoryContents = try FileManager.default.contentsOfDirectory(atPath: atPath)
-            for fileName in directoryContents {
-                if fileName.first != "." {
-                    itemsSource.append(fileName)
-                }
-            }
-        } catch {
-            print(error.localizedDescription)
-        }
-
-        itemsSource = itemsSource.sorted()
-
-        collectionViewSource.reloadData()
-
-        // Save button
-        if imagesDestination.count == 0 {
-            save.isEnabled = false
-        } else {
-            save.isEnabled = true
-        }
-    }
-
-    // MARK: Private Methods
-
-    func filter(image: UIImage) -> UIImage? {
-
-        var inputContrast: Double = 0
-
-        if filter == typeFilter.original {
-            return image
-        }
-
-        if filter == typeFilter.grayScale {
-            inputContrast = 1
-        }
-
-        if filter == typeFilter.bn {
-            inputContrast = 4
-        }
-
-        let ciImage = CIImage(image: image)!
-        let imageFilter = ciImage.applyingFilter("CIColorControls", parameters: ["inputSaturation": 0, "inputContrast": inputContrast])
-
-        let context: CIContext = CIContext(options: nil)
-        let cgImage: CGImage = context.createCGImage(imageFilter, from: imageFilter.extent)!
-        let image: UIImage = UIImage(cgImage: cgImage)
-        return image
-    }
-
-    /// This method moves a cell from source indexPath to destination indexPath within the same collection view. It works for only 1 item. If multiple items selected, no reordering happens.
-    ///
-    /// - Parameters:
-    ///   - coordinator: coordinator obtained from performDropWith: UICollectionViewDropDelegate method
-    ///   - destinationIndexPath: indexpath of the collection view where the user drops the element
-    ///   - collectionView: collectionView in which reordering needs to be done.
-
-    private func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
-
-        let items = coordinator.items
-
-        if items.count == 1, let item = items.first, let sourceIndexPath = item.sourceIndexPath {
-
-            var dIndexPath = destinationIndexPath
-
-            if dIndexPath.row >= collectionView.numberOfItems(inSection: 0) {
-                dIndexPath.row = collectionView.numberOfItems(inSection: 0) - 1
-            }
-
-            collectionView.performBatchUpdates({
-
-                if collectionView === collectionViewDestination {
-
-                    imagesDestination.remove(at: sourceIndexPath.row)
-                    imagesDestination.insert(item.dragItem.localObject as! UIImage, at: dIndexPath.row)
-
-                    let fileName = itemsDestination[sourceIndexPath.row]
-                    itemsDestination.remove(at: sourceIndexPath.row)
-                    itemsDestination.insert(fileName, at: dIndexPath.row)
-
-                } else {
-
-                    itemsSource.remove(at: sourceIndexPath.row)
-                    itemsSource.insert(item.dragItem.localObject as! String, at: dIndexPath.row)
-                }
-
-                collectionView.deleteItems(at: [sourceIndexPath])
-                collectionView.insertItems(at: [dIndexPath])
-            })
-
-            coordinator.drop(items.first!.dragItem, toItemAt: dIndexPath)
-        }
-    }
-
-    /// This method copies a cell from source indexPath in 1st collection view to destination indexPath in 2nd collection view. It works for multiple items.
-    ///
-    /// - Parameters:
-    ///   - coordinator: coordinator obtained from performDropWith: UICollectionViewDropDelegate method
-    ///   - destinationIndexPath: indexpath of the collection view where the user drops the element
-    ///   - collectionView: collectionView in which reordering needs to be done.
-
-    private func copyItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
-        collectionView.performBatchUpdates({
-
-            var indexPaths: [IndexPath] = []
-
-            for (index, item) in coordinator.items.enumerated() {
-
-                let indexPath = IndexPath(row: destinationIndexPath.row + index, section: destinationIndexPath.section)
-
-                if collectionView === collectionViewDestination {
-
-                    let fileName = item.dragItem.localObject as! String
-                    let fileNamePathAt = CCUtility.getDirectoryScan() + "/" + fileName
-
-                    guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePathAt)) else {
-                        return
-                    }
-                    guard let image =  UIImage(data: data) else {
-                        return
-                    }
-
-                    imagesDestination.insert(image, at: indexPath.row)
-                    itemsDestination.insert(fileName, at: indexPath.row)
-
-                } else {
-
-                    // NOT PERMITTED
-                    return
-                }
-
-                indexPaths.append(indexPath)
-            }
-
-            collectionView.insertItems(at: indexPaths)
-        })
-    }
-
-    // MARK: - UIGestureRecognizerv - Paste
-
-    @objc func handleLongPressGesture(recognizer: UIGestureRecognizer) {
-
-        if recognizer.state == UIGestureRecognizer.State.began {
-
-            self.becomeFirstResponder()
-
-            let pasteboard = UIPasteboard.general
-
-            if let recognizerView = recognizer.view, let recognizerSuperView = recognizerView.superview, pasteboard.hasImages {
-
-                UIMenuController.shared.menuItems = [UIMenuItem(title: "Paste", action: #selector(pasteImage))]
-                UIMenuController.shared.setTargetRect(recognizerView.frame, in: recognizerSuperView)
-                UIMenuController.shared.setMenuVisible(true, animated: true)
-            }
-        }
-    }
-
-    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
-        if action == #selector(pasteImage) {
-            return true
-        }
-        return false
-    }
-
-    @objc func pasteImage() {
-
-        let pasteboard = UIPasteboard.general
-
-        if pasteboard.hasImages {
-
-            let fileName = CCUtility.createFileName("scan.png", fileDate: Date(), fileType: PHAssetMediaType.image, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: true)!
-            let fileNamePath = CCUtility.getDirectoryScan() + "/" + fileName
-
-            guard let image = pasteboard.image?.fixedOrientation() else {
-                return
-            }
-
-            do {
-                try image.pngData()?.write(to: NSURL.fileURL(withPath: fileNamePath), options: .atomic)
-            } catch {
-                return
-            }
-
-            loadImage()
-        }
-    }
-}
-
-// MARK: - UICollectionViewDataSource Methods
-
-@available(iOS 13.0, *)
-extension DragDropViewController: UICollectionViewDataSource {
-
-    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-
-        return collectionView == collectionViewSource ? itemsSource.count : imagesDestination.count
-    }
-
-    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-
-        if collectionView == collectionViewSource {
-
-            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! ScanCell
-
-            let fileNamePath = CCUtility.getDirectoryScan() + "/" + itemsSource[indexPath.row]
-
-            guard let data = try? Data(contentsOf: URL(fileURLWithPath: fileNamePath)) else {
-                return cell
-            }
-
-            guard var image = UIImage(data: data) else {
-                return cell
-            }
-
-            let imageWidthInPixels = image.size.width * image.scale
-            let imageHeightInPixels = image.size.height * image.scale
-
-            // 72 DPI
-            if imageWidthInPixels > 595 || imageHeightInPixels > 842 {
-                image = image.resizeImage(size: CGSize(width: 595, height: 842), isAspectRation: true) ?? image
-            }
-
-            cell.customImageView?.image = image
-            cell.delete.action(for: .touchUpInside) { sender in
-
-                let buttonPosition: CGPoint = (sender as! UIButton).convert(.zero, to: self.collectionViewSource)
-                if let indexPath = self.collectionViewSource.indexPathForItem(at: buttonPosition) {
-
-                    let fileNameAtPath = CCUtility.getDirectoryScan() + "/" + self.itemsSource[indexPath.row]
-                    CCUtility.removeFile(atPath: fileNameAtPath)
-                    self.itemsSource.remove(at: indexPath.row)
-
-                    self.collectionViewSource.reloadData()
-                }
-            }
-
-            return cell
-
-        } else {
-
-            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath) as! ScanCell
-
-            var image = imagesDestination[indexPath.row]
-
-            let imageWidthInPixels = image.size.width * image.scale
-            let imageHeightInPixels = image.size.height * image.scale
-
-            // 72 DPI 
-            if imageWidthInPixels > 595 || imageHeightInPixels > 842 {
-                image = image.resizeImage(size: CGSize(width: 595, height: 842), isAspectRation: true) ?? image
-            }
-
-            cell.customImageView?.image = self.filter(image: image)
-            cell.customLabel.text = NSLocalizedString("_scan_document_pdf_page_", comment: "") + " " + "\(indexPath.row+1)"
-            cell.delete.action(for: .touchUpInside) { sender in
-
-                let buttonPosition: CGPoint = (sender as! UIButton).convert(.zero, to: self.collectionViewDestination)
-                if let indexPath = self.collectionViewDestination.indexPathForItem(at: buttonPosition) {
-
-                    self.imagesDestination.remove(at: indexPath.row)
-                    self.itemsDestination.remove(at: indexPath.row)
-
-                    self.collectionViewDestination.reloadData()
-
-                    // Save button
-                    if self.imagesDestination.count == 0 {
-                        self.save.isEnabled = false
-                    } else {
-                        self.save.isEnabled = true
-                    }
-                }
-            }
-            cell.rotate.action(for: .touchUpInside) { sender in
-
-                let buttonPosition: CGPoint = (sender as! UIButton).convert(.zero, to: self.collectionViewDestination)
-                if let indexPath = self.collectionViewDestination.indexPathForItem(at: buttonPosition) {
-
-                    let image = self.imagesDestination[indexPath.row]
-                    self.imagesDestination[indexPath.row] = image.rotate(radians: .pi/2)!
-
-                    self.collectionViewDestination.reloadData()
-                }
-            }
-
-            return cell
-        }
-    }
-}
-
-extension UIImage {
-    func rotate(radians: Float) -> UIImage? {
-        var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
-        // Trim off the extremely small float value to prevent core graphics from rounding it up
-        newSize.width = floor(newSize.width)
-        newSize.height = floor(newSize.height)
-
-        UIGraphicsBeginImageContextWithOptions(newSize, true, self.scale)
-        let context = UIGraphicsGetCurrentContext()!
-
-        // Move origin to middle
-        context.translateBy(x: newSize.width/2, y: newSize.height/2)
-        // Rotate around middle
-        context.rotate(by: CGFloat(radians))
-        // Draw the image at its center
-        self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))
-
-        let newImage = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-
-        return newImage
-    }
-}
-
-// MARK: - UICollectionViewDragDelegate Methods
-
-@available(iOS 13.0, *)
-extension DragDropViewController: UICollectionViewDragDelegate {
-    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
-
-        if collectionView == collectionViewSource {
-            let item = itemsSource[indexPath.row]
-            let itemProvider = NSItemProvider(object: item as NSString)
-            let dragItem = UIDragItem(itemProvider: itemProvider)
-
-            dragItem.localObject = item
-
-            return [dragItem]
-
-        } else {
-            let item = imagesDestination[indexPath.row]
-            let itemProvider = NSItemProvider(object: item as UIImage)
-            let dragItem = UIDragItem(itemProvider: itemProvider)
-
-            dragItem.localObject = item
-
-            return [dragItem]
-        }
-    }
-
-    func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
-
-        if collectionView == collectionViewSource {
-            let item = itemsSource[indexPath.row]
-            let itemProvider = NSItemProvider(object: item as NSString)
-            let dragItem = UIDragItem(itemProvider: itemProvider)
-
-            dragItem.localObject = item
-
-            return [dragItem]
-
-        } else {
-            let item = imagesDestination[indexPath.row]
-            let itemProvider = NSItemProvider(object: item as UIImage)
-            let dragItem = UIDragItem(itemProvider: itemProvider)
-
-            dragItem.localObject = item
-
-            return [dragItem]
-        }
-    }
-
-    func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
-
-        let previewParameters = UIDragPreviewParameters()
-        if collectionView == collectionViewSource {
-            previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 100, height: 100))
-        } else {
-            previewParameters.visiblePath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 80, height: 80))
-        }
-
-        return previewParameters
-    }
-}
-
-// MARK: - UICollectionViewDropDelegate Methods
-
-@available(iOS 13.0, *)
-extension DragDropViewController: UICollectionViewDropDelegate {
-
-    func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
-
-        return true // session.canLoadObjects(ofClass: NSString.self)
-    }
-
-    func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
-
-        if collectionView == collectionViewSource {
-
-            if collectionView.hasActiveDrag {
-                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
-            } else {
-                return UICollectionViewDropProposal(operation: .forbidden)
-            }
-
-        } else {
-
-            if collectionView.hasActiveDrag {
-                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
-            } else {
-                return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
-            }
-        }
-    }
-
-    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
-
-        let destinationIndexPath: IndexPath
-
-        switch coordinator.proposal.operation {
-
-        case .move:
-
-            if let indexPath = coordinator.destinationIndexPath {
-
-                destinationIndexPath = indexPath
-
-            } else {
-
-                // Get last index path of table view.
-                let section = collectionView.numberOfSections - 1
-                let row = collectionView.numberOfItems(inSection: section)
-
-                destinationIndexPath = IndexPath(row: row, section: section)
-            }
-            self.reorderItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
-
-            break
-
-        case .copy:
-
-            // Get last index path of table view.
-            let section = collectionView.numberOfSections - 1
-            let row = collectionView.numberOfItems(inSection: section)
-
-            destinationIndexPath = IndexPath(row: row, section: section)
-            self.copyItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
-
-            break
-
-        default:
-            return
-        }
-    }
-
-    func collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession) {
-
-        collectionViewDestination.reloadData()
-
-        // Save button
-        if imagesDestination.count == 0 {
-            save.isEnabled = false
-        } else {
-            save.isEnabled = true
-        }
-    }
-}

+ 1 - 1
iOSClient/Security/NCEndToEndEncryption.m

@@ -376,7 +376,7 @@
 - (NSString *)decryptPrivateKey:(NSString *)privateKeyCipher passphrase:(NSString *)passphrase publicKey:(NSString *)publicKey
 {
     NSMutableData *privateKeyData = [NSMutableData new];
-    NSString *privateKey;
+    NSString *privateKey = @"";
     
     // Key (data)
     NSMutableData *keyData = [NSMutableData dataWithLength:PBKDF2_KEY_LENGTH/8];

+ 2 - 2
iOSClient/Select/NCSelect.swift

@@ -520,7 +520,7 @@ extension NCSelect: UICollectionViewDataSource {
                 // image local
                 if dataSource.metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                } else if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
                 }
             }
@@ -617,7 +617,7 @@ extension NCSelect: UICollectionViewDataSource {
                 // image Local
                 if dataSource.metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                } else if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
                 }
             }

+ 3 - 3
iOSClient/Settings/CCManageAccount.m

@@ -235,14 +235,14 @@
     }
     
     // Web
-    if ([activeAccount.webpage length] > 0) {
+    if ([activeAccount.website length] > 0) {
         row = [XLFormRowDescriptor formRowDescriptorWithTag:@"userweb" rowType:XLFormRowDescriptorTypeInfo title:NSLocalizedString(@"_user_web_", nil)];
         row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground;
         [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
         [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
         [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"];
         [row.cellConfig setObject:[[UIImage imageNamed:@"network"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"];
-        row.value = activeAccount.webpage;
+        row.value = activeAccount.website;
         [section addFormRow:row];
     }
     
@@ -305,7 +305,7 @@
         [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"detailTextLabel.font"];
         [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"];
         [row.cellConfig setObject:[[UIImage imageNamed:@"company"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"];
-        row.value = activeAccount.company;
+        row.value = activeAccount.organisation;
         [section addFormRow:row];
     
         if (activeAccount.hcIsTrial) {

BIN
iOSClient/Supporting Files/af.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/af.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ar.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/ar.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ast.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/ast.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/az.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/az.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/be.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/be.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bg_BG.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bn_BD.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/br.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/br.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bs.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/bs.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ca.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/ca.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/cs-CZ.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/cy_GB.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/da.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/da.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/de.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/de.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/el.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/el.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/en-GB.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/en-GB.lproj/Localizable.strings


+ 7 - 0
iOSClient/Supporting Files/en.lproj/InfoPlist.strings

@@ -0,0 +1,7 @@
+NSCameraUsageDescription = "Camera access is required to scan documents and make photo and video.";
+NSFaceIDUsageDescription = "Face ID is required to authenticate using face recognition.";
+NSLocationAlwaysUsageDescription = "GPS is used to detect new photos from camera roll, continued use of GPS running in the background can dramatically decrease battery life.";
+NSPhotoLibraryUsageDescription = "Photo library access is required to upload your photos and videos to your cloud.";
+NSPhotoLibraryAddUsageDescription = "Photo library access is required to upload your photos and videos to your cloud.";
+NSMicrophoneUsageDescription = "Microphone access is required to create voice notes.";
+NSLocationWhenInUseUsageDescription = "GPS is used to detect new photos from camera roll on background, the use of GPS only when the App is in use is useless.";

+ 4 - 3
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -21,6 +21,7 @@
 //
 
 "_cancel_"                  = "Cancel";
+"_tap_to_cancel_"           = "Tap to cancel";
 "_upload_file_"             = "Upload file";
 "_accessibility_add_upload_" = "Add and upload";
 "_download_file_"           = "Download file";
@@ -333,8 +334,7 @@
 "_user_employee_"               = "Employee";
 "_user_contractor_"             = "Contractor";
 "_user_editprofile_"            = "Edit profile";
-"_favorite_offline_"            = "Favorites available offline";
-"_favorite_offline_footer_"     = "Making all favorites available offline may take a while and use a lot of memory while doing it.";
+"_select_offline_warning_"      = "Making multiple files and folders available offline may take a while and use a lot of memory while doing so.";
 "_advanced_"                    = "Advanced";
 "_disable_files_app_"           = "Disable Files App integration";
 "_disable_files_app_footer_"    = "Do not permit the access of files via the iOS Files application";
@@ -708,10 +708,11 @@
 "_trash_view_"                      = "Deleted files";
 "_trash_restore_all_"               = "Restore all files";
 "_trash_delete_all_"                = "Empty trash";
+"_trash_delete_all_description_"    = "Do you want to empty the trash bin?";
 "_trash_no_trash_"                  = "No files deleted";
 "_trash_no_trash_description_"      = "You can restore deleted files from here";
 "_trash_restore_selected_"          = "Restore selected files";
-"_trash_delete_selected_"           = "Deleted selected files";
+"_trash_delete_selected_"           = "Delete selected files";
 "_manage_file_offline_"             = "Manage offline files";
 "_set_available_offline_"           = "Set as available offline";
 "_remove_available_offline_"        = "Remove as available offline";

BIN
iOSClient/Supporting Files/eo.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/eo.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-419.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-AR.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-AR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CL.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-CL.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CR.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-CR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-EC.lproj/InfoPlist.strings


BIN
iOSClient/Supporting Files/es-EC.lproj/Localizable.strings


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно