浏览代码

Merge pull request #2196 from nextcloud/develop

Version 4.5
Marino Faggiana 2 年之前
父节点
当前提交
cc64ac49e9
共有 100 个文件被更改,包括 3223 次插入720 次删除
  1. 12 1
      .github/workflows/xcode.yml
  2. 10 1
      .swiftlint.yml
  3. 6 8
      File Provider Extension/FileProviderData.swift
  4. 5 5
      File Provider Extension/FileProviderEnumerator.swift
  5. 13 13
      File Provider Extension/FileProviderExtension+Actions.swift
  6. 19 13
      File Provider Extension/FileProviderExtension+Thumbnail.swift
  7. 11 11
      File Provider Extension/FileProviderExtension.swift
  8. 2 2
      File Provider Extension/FileProviderItem.swift
  9. 0 47
      MDM/AppConfig/specfile.xml
  10. 445 62
      Nextcloud.xcodeproj/project.pbxproj
  11. 2 1
      Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme
  12. 1 1
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme
  13. 97 0
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Notification Service Extension.xcscheme
  14. 2 1
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme
  15. 124 0
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Widget.xcscheme
  16. 5 5
      NextcloudTests/SharePermissionTest.swift
  17. 2 2
      README.md
  18. 4 4
      Share/NCShareCell.swift
  19. 2 2
      Share/NCShareExtension+DataSource.swift
  20. 3 3
      Share/NCShareExtension+Files.swift
  21. 3 4
      Share/NCShareExtension+NCDelegate.swift
  22. 18 18
      Share/NCShareExtension.swift
  23. 11 0
      Widget/Assets.xcassets/AccentColor.colorset/Contents.json
  24. 11 0
      Widget/Assets.xcassets/AppIcon.imageset/Contents.json
  25. 6 0
      Widget/Assets.xcassets/Contents.json
  26. 11 0
      Widget/Assets.xcassets/WidgetBackground.colorset/Contents.json
  27. 12 0
      Widget/Assets.xcassets/activity.imageset/Contents.json
  28. 二进制
      Widget/Assets.xcassets/activity.imageset/activity.png
  29. 12 0
      Widget/Assets.xcassets/widget.imageset/Contents.json
  30. 二进制
      Widget/Assets.xcassets/widget.imageset/icons8-widgetsmith-250.png
  31. 223 0
      Widget/Dashboard/DashboardData.swift
  32. 53 0
      Widget/Dashboard/DashboardWidgetProvider.swift
  33. 182 0
      Widget/Dashboard/DashboardWidgetView.swift
  34. 169 0
      Widget/Dashboard/Intent/Base.lproj/Dashboard.intentdefinition
  35. 8 0
      Widget/Dashboard/Intent/ca.lproj/Dashboard.strings
  36. 8 0
      Widget/Dashboard/Intent/cs-CZ.lproj/Dashboard.strings
  37. 8 0
      Widget/Dashboard/Intent/da.lproj/Dashboard.strings
  38. 8 0
      Widget/Dashboard/Intent/de.lproj/Dashboard.strings
  39. 8 0
      Widget/Dashboard/Intent/en-GB.lproj/Dashboard.strings
  40. 8 0
      Widget/Dashboard/Intent/en.lproj/Dashboard.strings
  41. 8 0
      Widget/Dashboard/Intent/es-419.lproj/Dashboard.strings
  42. 8 0
      Widget/Dashboard/Intent/es-CL.lproj/Dashboard.strings
  43. 8 0
      Widget/Dashboard/Intent/es-CO.lproj/Dashboard.strings
  44. 8 0
      Widget/Dashboard/Intent/es-CR.lproj/Dashboard.strings
  45. 8 0
      Widget/Dashboard/Intent/es-DO.lproj/Dashboard.strings
  46. 8 0
      Widget/Dashboard/Intent/es-EC.lproj/Dashboard.strings
  47. 8 0
      Widget/Dashboard/Intent/es-GT.lproj/Dashboard.strings
  48. 8 0
      Widget/Dashboard/Intent/es-HN.lproj/Dashboard.strings
  49. 8 0
      Widget/Dashboard/Intent/es-MX.lproj/Dashboard.strings
  50. 8 0
      Widget/Dashboard/Intent/es-NI.lproj/Dashboard.strings
  51. 8 0
      Widget/Dashboard/Intent/es-PA.lproj/Dashboard.strings
  52. 8 0
      Widget/Dashboard/Intent/es-PE.lproj/Dashboard.strings
  53. 8 0
      Widget/Dashboard/Intent/es-PR.lproj/Dashboard.strings
  54. 8 0
      Widget/Dashboard/Intent/es-PY.lproj/Dashboard.strings
  55. 8 0
      Widget/Dashboard/Intent/es-SV.lproj/Dashboard.strings
  56. 8 0
      Widget/Dashboard/Intent/es-UY.lproj/Dashboard.strings
  57. 8 0
      Widget/Dashboard/Intent/es.lproj/Dashboard.strings
  58. 8 0
      Widget/Dashboard/Intent/eu.lproj/Dashboard.strings
  59. 8 0
      Widget/Dashboard/Intent/fr.lproj/Dashboard.strings
  60. 8 0
      Widget/Dashboard/Intent/gl.lproj/Dashboard.strings
  61. 8 0
      Widget/Dashboard/Intent/hu.lproj/Dashboard.strings
  62. 8 0
      Widget/Dashboard/Intent/is.lproj/Dashboard.strings
  63. 8 0
      Widget/Dashboard/Intent/it.lproj/Dashboard.strings
  64. 8 0
      Widget/Dashboard/Intent/ja-JP.lproj/Dashboard.strings
  65. 8 0
      Widget/Dashboard/Intent/ka-GE.lproj/Dashboard.strings
  66. 8 0
      Widget/Dashboard/Intent/ko.lproj/Dashboard.strings
  67. 8 0
      Widget/Dashboard/Intent/nb-NO.lproj/Dashboard.strings
  68. 8 0
      Widget/Dashboard/Intent/nl.lproj/Dashboard.strings
  69. 8 0
      Widget/Dashboard/Intent/pl.lproj/Dashboard.strings
  70. 8 0
      Widget/Dashboard/Intent/pt-BR.lproj/Dashboard.strings
  71. 8 0
      Widget/Dashboard/Intent/pt-PT.lproj/Dashboard.strings
  72. 8 0
      Widget/Dashboard/Intent/ru.lproj/Dashboard.strings
  73. 8 0
      Widget/Dashboard/Intent/sk-SK.lproj/Dashboard.strings
  74. 8 0
      Widget/Dashboard/Intent/sr.lproj/Dashboard.strings
  75. 8 0
      Widget/Dashboard/Intent/sv.lproj/Dashboard.strings
  76. 8 0
      Widget/Dashboard/Intent/tr.lproj/Dashboard.strings
  77. 8 0
      Widget/Dashboard/Intent/zh-Hans.lproj/Dashboard.strings
  78. 8 0
      Widget/Dashboard/Intent/zh-Hant-TW.lproj/Dashboard.strings
  79. 272 0
      Widget/Files/FilesData.swift
  80. 50 0
      Widget/Files/FilesWidgetProvider.swift
  81. 169 0
      Widget/Files/FilesWidgetView.swift
  82. 95 0
      Widget/Lockscreen/LockscreenData.swift
  83. 47 0
      Widget/Lockscreen/LockscreenWidgetProvider.swift
  84. 75 0
      Widget/Lockscreen/LockscreenWidgetView.swift
  85. 44 0
      Widget/Toolbar/ToolbarData.swift
  86. 47 0
      Widget/Toolbar/ToolbarWidgetProvider.swift
  87. 118 0
      Widget/Toolbar/ToolbarWidgetView.swift
  88. 5 0
      Widget/Widget-Brinding-header.h
  89. 93 0
      Widget/Widget.swift
  90. 42 0
      WidgetDashboardIntentHandler/IntentHandler.swift
  91. 5 0
      WidgetDashboardIntentHandler/WidgetDashboardIntentHandler-Brinding-header.h
  92. 7 6
      iOSClient/.tx/config
  93. 4 4
      iOSClient/Account Request/NCAccountRequest.swift
  94. 36 32
      iOSClient/Activity/NCActivity.swift
  95. 1 1
      iOSClient/Activity/NCActivityCommentView.swift
  96. 33 46
      iOSClient/Activity/NCActivityTableViewCell.swift
  97. 193 158
      iOSClient/AppDelegate.swift
  98. 9 21
      iOSClient/Brand/Intro/NCIntroViewController.swift
  99. 42 238
      iOSClient/Brand/NCBrand.swift
  100. 8 10
      iOSClient/Brand/NCBridgeSwift.h

+ 12 - 1
.github/workflows/xcode.yml

@@ -54,4 +54,15 @@ jobs:
       run: |
         xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
       env:
-          SCHEME: Notification Service Extension
+          SCHEME: Notification Service Extension
+    - name: Build iOS Widget
+      run: |
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
+      env:
+          SCHEME: Widget
+    - name: Build iOS Widget Dashboard IntentHandler
+      run: |
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
+      env:
+          SCHEME: WidgetDashboardIntentHandler
+          

+ 10 - 1
.swiftlint.yml

@@ -41,6 +41,14 @@ excluded:
   - File Provider Extension/FileProviderExtension.swift
   - File Provider Extension/FileProviderUtility.swift
   - Notification Service Extension/NotificationService.swift
+  - Widget/Widget.swift
+  - Widget/Dashboard/DashboardData.swift
+  - Widget/Dashboard/DashboardWidgetView.swift
+  - Widget/Files/FilesData.swift
+  - Widget/Files/FilesWidgetView.swift
+  - Widget/Lockscreen/LockscreenData.swift
+  - Widget/Lockscreen/LockscreenWidgetView.swift
+  - Widget/Lockscreen/LockscreenWidgetProvider.swift
   - iOSClient/Activity/NCActivity.swift
   - iOSClient/Activity/NCActivityTableViewCell.swift
   - iOSClient/AppDelegate.swift
@@ -56,6 +64,7 @@ excluded:
   - iOSClient/Data/NCManageDatabase.swift
   - iOSClient/Data/NCManageDatabase+Metadata.swift
   - iOSClient/Data/NCManageDatabase+Video.swift
+  - iOSClient/Data/NCManageDatabase+DashboardWidget.swift
   - iOSClient/Diagnostics/NCCapabilitiesViewController.swift
   - iOSClient/EmptyView/NCEmptyDataSet.swift
   - iOSClient/Extensions/UIColor+Extensions.swift
@@ -122,6 +131,7 @@ excluded:
   - iOSClient/Utility/NCStoreReview.swift
   - iOSClient/Utility/NCUtility.swift
   - iOSClient/Utility/NCUtilityFileSystem.swift
+  - iOSClient/Utility/NCUtilityGUI.swift
   - iOSClient/Viewer/NCViewer.swift
   - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift
   - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift
@@ -133,5 +143,4 @@ excluded:
   - iOSClient/Viewer/NCViewerProviderContextMenu.swift
   - iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift
 
-
 reporter: "xcode"

+ 6 - 8
File Provider Extension/FileProviderData.swift

@@ -22,7 +22,7 @@
 //
 
 import UIKit
-import NCCommunication
+import NextcloudKit
 
 class fileProviderData: NSObject {
     static let shared: fileProviderData = {
@@ -77,11 +77,11 @@ class fileProviderData: NSObject {
 
         // LOG
         if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
-            NCCommunicationCommon.shared.pathLog = pathDirectoryGroup
+            NKCommon.shared.pathLog = pathDirectoryGroup
             let levelLog = CCUtility.getLogLevel()
-            NCCommunicationCommon.shared.levelLog = levelLog
+            NKCommon.shared.levelLog = levelLog
             let version = NSString(format: NCBrandOptions.shared.textCopyrightNextcloudiOS as NSString, NCUtility.shared.getVersionApp()) as String
-            NCCommunicationCommon.shared.writeLog("Start File Provider session with level \(levelLog) " + version + " (File Provider Extension)")
+            NKCommon.shared.writeLog("[INFO] Start File Provider session with level \(levelLog) " + version + " (File Provider Extension)")
         }
 
         // NO DOMAIN -> Set default account
@@ -89,7 +89,6 @@ class fileProviderData: NSObject {
 
             guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return nil }
             let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: activeAccount.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
-            let webDav = NCUtilityFileSystem.shared.getWebDAV(account: activeAccount.account)
 
             account = activeAccount.account
             user = activeAccount.user
@@ -97,7 +96,7 @@ class fileProviderData: NSObject {
             accountUrlBase = activeAccount.urlBase
             homeServerUrl = NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account)
 
-            NCCommunicationCommon.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), webDav: webDav, nextcloudVersion: serverVersionMajor, delegate: NCNetworking.shared)
+            NKCommon.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), nextcloudVersion: serverVersionMajor, delegate: NCNetworking.shared)
             NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
             return tableAccount.init(value: activeAccount)
@@ -114,7 +113,6 @@ class fileProviderData: NSObject {
             if accountDomain == domain!.identifier.rawValue {
 
                 let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: activeAccount.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
-                let webDav = NCUtilityFileSystem.shared.getWebDAV(account: activeAccount.account)
 
                 account = activeAccount.account
                 user = activeAccount.user
@@ -122,7 +120,7 @@ class fileProviderData: NSObject {
                 accountUrlBase = activeAccount.urlBase
                 homeServerUrl = NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account)
 
-                NCCommunicationCommon.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), webDav: webDav, nextcloudVersion: serverVersionMajor, delegate: NCNetworking.shared)
+                NKCommon.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), nextcloudVersion: serverVersionMajor, delegate: NCNetworking.shared)
                 NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
                 return tableAccount.init(value: activeAccount)

+ 5 - 5
File Provider Extension/FileProviderEnumerator.swift

@@ -23,7 +23,7 @@
 
 import UIKit
 import FileProvider
-import NCCommunication
+import NextcloudKit
 
 class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
@@ -203,15 +203,15 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
             directoryEtag = tableDirectory.etag
         }
 
-        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, errorCode, _ in
+        NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, error in
 
             if directoryEtag != files.first?.etag {
 
-                NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, errorCode, _ in
+                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, error in
 
-                    if errorCode == 0 {
+                    if error == .success {
                         DispatchQueue.global().async {
-                            NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: true, account: account) { _, metadatasFolder, metadatas in
+                            NCManageDatabase.shared.convertNKFilesToMetadatas(files, useMetadataFolder: true, account: account) { _, metadatasFolder, metadatas in
                                 let metadatasResult = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal))
                                 NCManageDatabase.shared.updateMetadatas(metadatas, metadatasResult: metadatasResult)
                                 for metadata in metadatasFolder {

+ 13 - 13
File Provider Extension/FileProviderExtension+Actions.swift

@@ -23,7 +23,7 @@
 
 import UIKit
 import FileProvider
-import NCCommunication
+import NextcloudKit
 
 extension FileProviderExtension {
 
@@ -37,13 +37,13 @@ extension FileProviderExtension {
         let directoryName = NCUtilityFileSystem.shared.createFileName(directoryName, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
         let serverUrlFileName = tableDirectory.serverUrl + "/" + directoryName
 
-        NCCommunication.shared.createFolder(serverUrlFileName) { account, ocId, _, errorCode, _ in
+        NextcloudKit.shared.createFolder(serverUrlFileName) { account, ocId, _, error in
 
-            if errorCode == 0 {
+            if error == .success {
 
-                NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, errorCode, _ in
+                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, error in
 
-                    if errorCode == 0 && files.count > 0 {
+                    if error == .success && files.count > 0 {
 
                         let file = files.first!
                         let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: false, account: fileProviderData.shared.account)
@@ -88,9 +88,9 @@ extension FileProviderExtension {
         let serverUrl = metadata.serverUrl
         let fileName = metadata.fileName
 
-        NCCommunication.shared.deleteFileOrFolder(serverUrlFileName) { account, errorCode, _ in
+        NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName) { account, error in
 
-            if errorCode == 0 { // || error == kOCErrorServerPathNotFound {
+            if error == .success { // || error == kOCErrorServerPathNotFound {
 
                 let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(itemIdentifier.rawValue)!
                 do {
@@ -138,9 +138,9 @@ extension FileProviderExtension {
         let serverUrlTo = tableDirectoryTo.serverUrl
         let fileNameTo = serverUrlTo + "/" + itemFrom.filename
 
-        NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: false) { account, errorCode, _ in
+        NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: false) { account, error in
 
-            if errorCode == 0 {
+            if error == .success {
 
                 if metadataFrom.directory {
                     NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: serverUrlFrom, account: account)
@@ -180,9 +180,9 @@ extension FileProviderExtension {
         let fileNamePathTo = metadata.serverUrl + "/" + itemName
         let ocId = metadata.ocId
 
-        NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false) { account, errorCode, _ in
+        NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false) { account, error in
 
-            if errorCode == 0 {
+            if error == .success {
 
                 // Rename metadata
                 NCManageDatabase.shared.renameMetadata(fileNameTo: itemName, ocId: ocId)
@@ -247,9 +247,9 @@ extension FileProviderExtension {
         if (favorite == true && metadata.favorite == false) || (favorite == false && metadata.favorite == true) {
             let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)!
 
-            NCCommunication.shared.setFavorite(fileName: fileNamePath, favorite: favorite) { _, errorCode, _ in
+            NextcloudKit.shared.setFavorite(fileName: fileNamePath, favorite: favorite) { _, error in
 
-                if errorCode == 0 {
+                if error == .success {
 
                     guard let metadataTemp = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
                         completionHandler(nil, NSFileProviderError(.noSuchItem))

+ 19 - 13
File Provider Extension/FileProviderExtension+Thumbnail.swift

@@ -23,7 +23,7 @@
 
 import UIKit
 import FileProvider
-import NCCommunication
+import NextcloudKit
 
 extension FileProviderExtension {
 
@@ -45,20 +45,26 @@ extension FileProviderExtension {
                 let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)!
                 let fileNameIconLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)!
 
-                NCCommunication.shared.getPreview(fileNamePath: fileNamePath, widthPreview: NCGlobal.shared.sizeIcon, heightPreview: NCGlobal.shared.sizeIcon) { _, data, errorCode, _ in
-                    if errorCode == 0 && data != nil {
-                        do {
-                            try data!.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic)
-                        } catch { }
-                        perThumbnailCompletionHandler(itemIdentifier, data, nil)
-                    } else {
-                        perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
-                    }
-                    counterProgress += 1
-                    if counterProgress == progress.totalUnitCount {
-                        completionHandler(nil)
+                if let urlBase = metadata.urlBase.urlEncoded,
+                   let fileNamePath = fileNamePath.urlEncoded,
+                   let url = URL(string: "\(urlBase)/index.php/core/preview.png?file=\(fileNamePath)&x=\(size)&y=\(size)&a=1&mode=cover") {
+
+                    NextcloudKit.shared.getPreview(url: url) { _, data, error in
+                        if error == .success && data != nil {
+                            do {
+                                try data!.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic)
+                            } catch { }
+                            perThumbnailCompletionHandler(itemIdentifier, data, nil)
+                        } else {
+                            perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
+                        }
+                        counterProgress += 1
+                        if counterProgress == progress.totalUnitCount {
+                            completionHandler(nil)
+                        }
                     }
                 }
+
             } else {
                 counterProgress += 1
                 if counterProgress == progress.totalUnitCount {

+ 11 - 11
File Provider Extension/FileProviderExtension.swift

@@ -23,7 +23,7 @@
 
 import UIKit
 import FileProvider
-import NCCommunication
+import NextcloudKit
 import Alamofire
 
 /* -----------------------------------------------------------------------------------------------------------------------------------------------
@@ -119,7 +119,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
             metadata.fileName = "root"
             metadata.fileNameView = "root"
             metadata.serverUrl = fileProviderData.shared.homeServerUrl
-            metadata.classFile = NCCommunicationCommon.typeClassFile.directory.rawValue
+            metadata.classFile = NKCommon.typeClassFile.directory.rawValue
 
             return FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier(NSFileProviderItemIdentifier.rootContainer.rawValue))
 
@@ -209,7 +209,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusDownloading)
         fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, update: true)
 
-        NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
+        NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
 
         }, taskHandler: { task in
 
@@ -218,7 +218,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
 
         }, progressHandler: { _ in
 
-        }) { _, etag, date, _, _, _, errorCode, errorDescription in
+        }) { _, etag, date, _, _, _, error in
 
             self.outstandingSessionTasks.removeValue(forKey: url)
             guard var metadata = fileProviderUtility.shared.getTableMetadataFromItemIdentifier(identifier) else {
@@ -227,7 +227,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
             }
             metadata = tableMetadata.init(value: metadata)
 
-            if errorCode == 0 {
+            if error == .success {
 
                 metadata.status = NCGlobal.shared.metadataStatusNormal
                 metadata.date = date ?? NSDate()
@@ -238,7 +238,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
 
                 completionHandler(nil)
 
-            } else if errorCode == 200 {
+            } else if error.errorCode == 200 {
 
                 NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusNormal)
 
@@ -247,7 +247,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
             } else {
 
                 metadata.status = NCGlobal.shared.metadataStatusDownloadError
-                metadata.sessionError = errorDescription
+                metadata.sessionError = error.errorDescription
                 NCManageDatabase.shared.addMetadata(metadata)
 
                 completionHandler(NSFileProviderError(.noSuchItem))
@@ -277,7 +277,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         let serverUrlFileName = metadata.serverUrl + "/" + fileName
         let fileNameLocalPath = url.path
 
-        if let task = NCCommunicationBackground.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, description: metadata.ocId, session: NCNetworking.shared.sessionManagerBackgroundExtension) {
+        if let task = NKBackground.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, description: metadata.ocId, session: NCNetworking.shared.sessionManagerBackgroundExtension) {
 
             fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(metadata.fileId)) { _ in }
         }
@@ -357,7 +357,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
                 let serverUrlFileName = tableDirectory.serverUrl + "/" + fileName
                 let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName)!
 
-                if let task = NCCommunicationBackground.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, description: ocIdTemp, session: NCNetworking.shared.sessionManagerBackgroundExtension) {
+                if let task = NKBackground.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, description: ocIdTemp, session: NCNetworking.shared.sessionManagerBackgroundExtension) {
 
                     self.outstandingSessionTasks[URL(fileURLWithPath: fileNameLocalPath)] = task as URLSessionTask
 
@@ -371,7 +371,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         }
     }
 
-    func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: NSDate?, size: Int64, description: String?, task: URLSessionTask, errorCode: Int, errorDescription: String) {
+    func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: NSDate?, size: Int64, description: String?, task: URLSessionTask, error: NKError) {
 
         guard let ocIdTemp = description else { return }
         guard let metadataTemp = NCManageDatabase.shared.getMetadataFromOcId(ocIdTemp) else { return }
@@ -383,7 +383,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         }
         outstandingOcIdTemp[ocIdTemp] = ocId
 
-        if errorCode == 0 {
+        if error == .success {
 
             // New file
             if ocId != ocIdTemp {

+ 2 - 2
File Provider Extension/FileProviderItem.swift

@@ -23,7 +23,7 @@
 
 import UIKit
 import FileProvider
-import NCCommunication
+import NextcloudKit
 
 class FileProviderItem: NSObject, NSFileProviderItem {
 
@@ -43,7 +43,7 @@ class FileProviderItem: NSObject, NSFileProviderItem {
     }
 
     var typeIdentifier: String {
-        let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: "", directory: metadata.directory)
+        let results = NKCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: "", directory: metadata.directory)
         return results.typeIdentifier
     }
 

+ 0 - 47
MDM/AppConfig/specfile.xml

@@ -1,47 +0,0 @@
-<managedAppConfiguration>
-	<version>1.0.0</version>
-	<bundleId>it.twsweb.Nextcloud</bundleId>
-	<dict>
-		<string keyName="serverUrl">
-			<defaultValue>
-				<value>https:&#x2F;&#x2F;cloud.nextcloud.com</value>
-			</defaultValue>
-		</string>
-		<string keyName="username">
-			<defaultValue>
-				<value>marino.faggiana</value>
-			</defaultValue>
-		</string>
-		<string keyName="password">
-			<defaultValue>
-				<value>password</value>
-			</defaultValue>
-		</string>
-	</dict>
-	<presentation defaultLocale="en-US">
-		<field keyName="serverUrl" type="input">
-			<label>
-				<language value="en-US">serverUrl</language>
-			</label>
-			<description>
-				<language value="en-US">Nextcloud server url</language>
-			</description>
-		</field>
-		<field keyName="username" type="input">
-			<label>
-				<language value="en-US">username</language>
-			</label>
-			<description>
-				<language value="en-US">User Name</language>
-			</description>
-		</field>
-		<field keyName="password" type="input">
-			<label>
-				<language value="en-US">password</language>
-			</label>
-			<description>
-				<language value="en-US">Password</language>
-			</description>
-		</field>
-	</presentation>
-</managedAppConfiguration>

文件差异内容过多而无法显示
+ 445 - 62
Nextcloud.xcodeproj/project.pbxproj


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

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1330"
+   LastUpgradeVersion = "1400"
    wasCreatedForAppExtension = "YES"
    version = "2.0">
    <BuildAction
@@ -93,6 +93,7 @@
       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 = "1330"
+   LastUpgradeVersion = "1400"
    version = "1.7">
    <BuildAction
       parallelizeBuildables = "YES"

+ 97 - 0
Nextcloud.xcodeproj/xcshareddata/xcschemes/Notification Service Extension.xcscheme

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1400"
+   wasCreatedForAppExtension = "YES"
+   version = "2.0">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "2C33C47E23E2C475005F963B"
+               BuildableName = "Notification Service Extension.appex"
+               BlueprintName = "Notification Service Extension"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+               BuildableName = "Nextcloud.app"
+               BlueprintName = "Nextcloud"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = ""
+      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
+      launchStyle = "0"
+      askForAppToLaunch = "Yes"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      askForAppToLaunch = "Yes"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

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

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

+ 124 - 0
Nextcloud.xcodeproj/xcshareddata/xcschemes/Widget.xcscheme

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1400"
+   wasCreatedForAppExtension = "YES"
+   version = "2.0">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F7346E0F28B0EF5B006CE2D2"
+               BuildableName = "Widget.appex"
+               BlueprintName = "Widget"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+               BuildableName = "Nextcloud.app"
+               BlueprintName = "Nextcloud"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = ""
+      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
+      launchStyle = "0"
+      askForAppToLaunch = "Yes"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES"
+      launchAutomaticallySubstyle = "2">
+      <RemoteRunnable
+         runnableDebuggingMode = "2"
+         BundleIdentifier = "com.apple.springboard">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F7346E0F28B0EF5B006CE2D2"
+            BuildableName = "Widget.appex"
+            BlueprintName = "Widget"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </RemoteRunnable>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <EnvironmentVariables>
+         <EnvironmentVariable
+            key = "_XCWidgetKind"
+            value = ""
+            isEnabled = "YES">
+         </EnvironmentVariable>
+         <EnvironmentVariable
+            key = "_XCWidgetDefaultView"
+            value = "timeline"
+            isEnabled = "YES">
+         </EnvironmentVariable>
+         <EnvironmentVariable
+            key = "_XCWidgetFamily"
+            value = "medium"
+            isEnabled = "YES">
+         </EnvironmentVariable>
+      </EnvironmentVariables>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      askForAppToLaunch = "Yes"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 5 - 5
NextcloudTests/SharePermissionTest.swift

@@ -23,7 +23,7 @@
 
 @testable import Nextcloud
 import XCTest
-import NCCommunication
+import NextcloudKit
 
 class SharePermissionTest: XCTestCase {
     override func setUp() {
@@ -35,7 +35,7 @@ class SharePermissionTest: XCTestCase {
     }
 
     func testShareCellPermissionCell() throws {
-        let share = NCTableShareOptions(sharee: NCCommunicationSharee(), metadata: tableMetadata(), password: nil)
+        let share = NCTableShareOptions(sharee: NKSharee(), metadata: tableMetadata(), password: nil)
         let shareConfig = NCShareConfig(parentMetadata: tableMetadata(), share: share)
 
         for row in 0..<shareConfig.permissions.count {
@@ -48,7 +48,7 @@ class SharePermissionTest: XCTestCase {
 
         let meta = tableMetadata()
         meta.sharePermissionsCollaborationServices = 31
-        let fullShare = NCTableShareOptions(sharee: NCCommunicationSharee(), metadata: meta, password: nil)
+        let fullShare = NCTableShareOptions(sharee: NKSharee(), metadata: meta, password: nil)
         let shareFullConfig = NCShareConfig(parentMetadata: meta, share: fullShare)
 
         for row in 0..<shareFullConfig.permissions.count {
@@ -102,7 +102,7 @@ class SharePermissionTest: XCTestCase {
     func testUserShare() throws {
         let meta = tableMetadata()
         meta.directory = false
-        let sharee = NCCommunicationSharee()
+        let sharee = NKSharee()
         let share = NCTableShareOptions(sharee: sharee, metadata: meta, password: nil)
         let fileConfig = NCShareConfig(parentMetadata: meta, share: share)
         XCTAssertEqual(fileConfig.advanced, NCShareDetails.forUser)
@@ -126,7 +126,7 @@ class SharePermissionTest: XCTestCase {
         XCTAssertEqual(fileConfig.permissions as? [NCLinkPermission], NCLinkPermission.forFile)
 
         meta.directory = true
-        let sharee = NCCommunicationSharee()
+        let sharee = NKSharee()
         let folderShare = NCTableShareOptions(sharee: sharee, metadata: meta, password: nil)
         let folderConfig = NCShareConfig(parentMetadata: meta, share: folderShare)
         XCTAssertEqual(folderConfig.resharePermission, meta.sharePermissionsCollaborationServices)

+ 2 - 2
README.md

@@ -2,7 +2,7 @@
 [![Releases](https://img.shields.io/github/release/nextcloud/ios.svg)](https://github.com/nextcloud/ios/releases/latest) [![Build](https://github.com/nextcloud/ios/actions/workflows/xcode.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/xcode.yml) [![SwiftLint](https://github.com/nextcloud/ios/actions/workflows/lint.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/lint.yml)
 [![irc](https://img.shields.io/badge/IRC-%23nextcloud--mobile%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-mobile)
 
-<img src="Animation.gif" alt="Demo of the Nextcloud iOS files app" width="277" height="600">
+<img src="Animation.gif" alt="Demo of the Nextcloud iOS files app" width="277" height="600"><img src="widget.png" alt="Widget of the Nextcloud iOS files app" width="277" height="600">
 
 [<img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg"
 alt="Demo of the Nextcloud iOS files app"
@@ -38,7 +38,7 @@ branch. Maybe start working on [starter issues](https://github.com/nextcloud/ios
 
 Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/ios/pulls)
 
-### Xcode 13.4 Project Setup
+### Xcode 14 Project Setup
 
 #### Dependencies
 

+ 4 - 4
Share/NCShareCell.swift

@@ -22,7 +22,7 @@
 //
 
 import UIKit
-import NCCommunication
+import NextcloudKit
 
 protocol NCShareCellDelegate: AnyObject {
     var uploadStarted: Bool { get }
@@ -40,9 +40,9 @@ class NCShareCell: UITableViewCell {
 
     func setup(fileName: String) {
         self.fileName = fileName
-        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
+        let resultInternalType = NKCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
 
-        backgroundColor = NCBrandColor.shared.systemBackground
+        backgroundColor = .systemBackground
         imageCell?.layer.cornerRadius = 6
         imageCell?.layer.masksToBounds = true
 
@@ -61,7 +61,7 @@ class NCShareCell: UITableViewCell {
         let fileSize = NCUtilityFileSystem.shared.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
         sizeCell?.text = CCUtility.transformedSize(fileSize)
 
-        moreButton?.setImage(NCUtility.shared.loadImage(named: "more").image(color: NCBrandColor.shared.label, size: 15), for: .normal)
+        moreButton?.setImage(NCUtility.shared.loadImage(named: "more").image(color: .label, size: 15), for: .normal)
     }
 
     @IBAction func buttonTapped(_ sender: Any) {

+ 2 - 2
Share/NCShareExtension+DataSource.swift

@@ -22,7 +22,7 @@
 //
 
 import UIKit
-import NCCommunication
+import NextcloudKit
 
 // MARK: - Collection View (target folder)
 
@@ -67,7 +67,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.fileObjectId = metadata.ocId
         cell.fileUser = metadata.ownerId
         cell.labelTitle.text = metadata.fileNameView
-        cell.labelTitle.textColor = NCBrandColor.shared.label
+        cell.labelTitle.textColor = .label
 
         cell.imageSelect.image = nil
         cell.imageStatus.image = nil

+ 3 - 3
Share/NCShareExtension+Files.swift

@@ -73,11 +73,11 @@ extension NCShareExtension {
         networkInProgress = true
         collectionView.reloadData()
 
-        NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { _, metadataFolder, _, _, _, _, errorCode, errorDescription in
+        NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { _, metadataFolder, _, _, _, _, error in
 
             DispatchQueue.main.async {
-                if errorCode != 0 {
-                    self.showAlert(description: errorDescription)
+                if error != .success {
+                    self.showAlert(description: error.errorDescription)
                 }
                 self.networkInProgress = false
                 self.metadataFolder = metadataFolder

+ 3 - 4
Share/NCShareExtension+NCDelegate.swift

@@ -21,7 +21,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
-import NCCommunication
+import NextcloudKit
 import UIKit
 
 extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate {
@@ -81,14 +81,13 @@ extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate {
         NCBrandColor.shared.createUserColors()
 
         // NETWORKING
-        NCCommunicationCommon.shared.setup(
+        NKCommon.shared.setup(
             account: activeAccount.account,
             user: activeAccount.user,
             userId: activeAccount.userId,
             password: CCUtility.getPassword(activeAccount.account),
             urlBase: activeAccount.urlBase,
             userAgent: CCUtility.getUserAgent(),
-            webDav: NCUtilityFileSystem.shared.getWebDAV(account: activeAccount.account),
             nextcloudVersion: 0,
             delegate: NCNetworking.shared)
 
@@ -122,7 +121,7 @@ extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCel
     func renameFile(named fileName: String) {
         guard let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile else { return }
 
-        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
+        let resultInternalType = NKCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
         vcRename.delegate = self
         vcRename.fileName = fileName
         if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) {

+ 18 - 18
Share/NCShareExtension.swift

@@ -24,7 +24,7 @@
 //
 
 import UIKit
-import NCCommunication
+import NextcloudKit
 import JGProgressHUD
 
 enum NCShareExtensionError: Error {
@@ -85,27 +85,27 @@ class NCShareExtension: UIViewController {
 
         collectionView.addSubview(refreshControl)
         refreshControl.tintColor = NCBrandColor.shared.brandText
-        refreshControl.backgroundColor = NCBrandColor.shared.systemBackground
+        refreshControl.backgroundColor = .systemBackground
         refreshControl.addTarget(self, action: #selector(reloadDatasource), for: .valueChanged)
 
-        commandView.backgroundColor = NCBrandColor.shared.secondarySystemBackground
-        separatorView.backgroundColor = NCBrandColor.shared.separator
+        commandView.backgroundColor = .secondarySystemBackground
+        separatorView.backgroundColor = .separator
         separatorHeightConstraint.constant = 0.5
 
-        tableView.separatorColor = NCBrandColor.shared.separator
+        tableView.separatorColor = .separator
         tableView.layer.cornerRadius = 10
         tableView.tableFooterView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 1)))
         commandViewHeightConstraint.constant = heightCommandView
 
         createFolderView.layer.cornerRadius = 10
-        createFolderImage.image = NCUtility.shared.loadImage(named: "folder.badge.plus", color: NCBrandColor.shared.label)
+        createFolderImage.image = NCUtility.shared.loadImage(named: "folder.badge.plus", color: .label)
         createFolderLabel.text = NSLocalizedString("_create_folder_", comment: "")
         let createFolderGesture = UITapGestureRecognizer(target: self, action: #selector(actionCreateFolder))
         createFolderView.addGestureRecognizer(createFolderGesture)
 
         uploadView.layer.cornerRadius = 10
 
-        // uploadImage.image = NCUtility.shared.loadImage(named: "square.and.arrow.up", color: NCBrandColor.shared.label)
+        // uploadImage.image = NCUtility.shared.loadImage(named: "square.and.arrow.up", color: .label)
         uploadLabel.text = NSLocalizedString("_upload_", comment: "")
         uploadLabel.textColor = .systemBlue
         let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload))
@@ -116,14 +116,14 @@ class NCShareExtension: UIViewController {
         let isSimulatorOrTestFlight = NCUtility.shared.isSimulatorOrTestFlight()
         let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())
 
-        NCCommunicationCommon.shared.levelLog = levelLog
+        NKCommon.shared.levelLog = levelLog
         if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
-            NCCommunicationCommon.shared.pathLog = pathDirectoryGroup
+            NKCommon.shared.pathLog = pathDirectoryGroup
         }
         if isSimulatorOrTestFlight {
-            NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
+            NKCommon.shared.writeLog("[INFO] Start Share session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
         } else {
-            NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS)
+            NKCommon.shared.writeLog("[INFO] Start Share session with level \(levelLog) " + versionNextcloudiOS)
         }
 
         // Colors
@@ -283,9 +283,9 @@ class NCShareExtension: UIViewController {
     }
 
     @objc func actionCreateFolder() {
-        let alertController = UIAlertController.createFolder(serverUrl: serverUrl, urlBase: activeAccount) { errorCode, errorDescription in
-            guard errorCode != 0 else { return }
-            self.showAlert(title: "_error_createsubfolders_upload_", description: errorDescription)
+        let alertController = UIAlertController.createFolder(serverUrl: serverUrl, urlBase: activeAccount) { error in
+            guard error != .success else { return }
+            self.showAlert(title: "_error_createsubfolders_upload_", description: error.errorDescription)
         }
         self.present(alertController, animated: true)
     }
@@ -312,7 +312,7 @@ extension NCShareExtension {
                 ocId: ocId,
                 serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "",
                 contentType: "")
-            metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload
+            metadata.session = NKCommon.shared.sessionIdentifierUpload
             metadata.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension
             metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)
             metadata.status = NCGlobal.shared.metadataStatusWaitUpload
@@ -339,7 +339,7 @@ extension NCShareExtension {
         guard uploadStarted else { return }
         guard uploadMetadata.count > counterUploaded else { return finishedUploading() }
         let metadata = uploadMetadata[counterUploaded]
-        let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false)
+        let results = NKCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false)
         metadata.contentType = results.mimeType
         metadata.iconName = results.iconName
         metadata.classFile = results.classFile
@@ -352,8 +352,8 @@ extension NCShareExtension {
         hud.progress = 0
         hud.show(in: self.view)
 
-        NCNetworking.shared.upload(metadata: metadata) { } completion: { errorCode, _ in
-            if errorCode != 0 {
+        NCNetworking.shared.upload(metadata: metadata) { } completion: { error in
+            if error != .success {
                 let path = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)!
                 NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
                 NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)

+ 11 - 0
Widget/Assets.xcassets/AccentColor.colorset/Contents.json

@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 11 - 0
Widget/Assets.xcassets/AppIcon.imageset/Contents.json

@@ -0,0 +1,11 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 6 - 0
Widget/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 11 - 0
Widget/Assets.xcassets/WidgetBackground.colorset/Contents.json

@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 12 - 0
Widget/Assets.xcassets/activity.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "activity.png",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

二进制
Widget/Assets.xcassets/activity.imageset/activity.png


+ 12 - 0
Widget/Assets.xcassets/widget.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-widgetsmith-250.png",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

二进制
Widget/Assets.xcassets/widget.imageset/icons8-widgetsmith-250.png


+ 223 - 0
Widget/Dashboard/DashboardData.swift

@@ -0,0 +1,223 @@
+//
+//  DashboardData.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 20/08/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 WidgetKit
+import NextcloudKit
+import RealmSwift
+import SVGKit
+
+struct DashboardDataEntry: TimelineEntry {
+    let date: Date
+    let datas: [DashboardData]
+    let dashboard: tableDashboardWidget?
+    let buttons: [tableDashboardWidgetButton]?
+    let isPlaceholder: Bool
+    let titleImage: UIImage
+    let title: String
+    let footerImage: String
+    let footerText: String
+}
+
+struct DashboardData: Identifiable, Hashable {
+    let id: Int
+    let title: String
+    let subTitle: String
+    let link: URL
+    let icon: UIImage
+    let template: Bool
+    let avatar: Bool
+}
+
+struct DashboardDataButton: Hashable {
+    let type: String
+    let Text: String
+    let link: String
+}
+
+let dashboardDatasTest: [DashboardData] = [
+    .init(id: 0, title: "title0", subTitle: "subTitle-description0", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 1, title: "title1", subTitle: "subTitle-description1", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 2, title: "title2", subTitle: "subTitle-description2", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 3, title: "title3", subTitle: "subTitle-description3", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 4, title: "title4", subTitle: "subTitle-description4", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 5, title: "title5", subTitle: "subTitle-description5", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 6, title: "title6", subTitle: "subTitle-description6", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 7, title: "title7", subTitle: "subTitle-description7", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 8, title: "title8", subTitle: "subTitle-description8", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false),
+    .init(id: 9, title: "title9", subTitle: "subTitle-description9", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false)
+]
+
+func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
+    
+    if withButton {
+        let height = Int((displaySize.height - 85) / 50)
+        return height
+    } else {
+        let height = Int((displaySize.height - 60) / 50)
+        return height
+    }
+}
+
+func getDashboardDataEntry(intent: Applications?, isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: DashboardDataEntry) -> Void) {
+
+    let dashboardItems = getDashboardItems(displaySize: displaySize, withButton: false)
+    let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems - 1])
+
+    if isPreview {
+        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard"))
+    }
+
+    guard let account = NCManageDatabase.shared.getActiveAccount() else {
+        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", comment: "")))
+    }
+
+    // Default widget
+    let result = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account).first
+    let id: String = intent?.identifier ?? (result?.id ?? "recommendations")
+
+    let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
+    guard serverVersionMajor >= NCGlobal.shared.nextcloudVersion25 else {
+        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "xmark.icloud", footerText: NSLocalizedString("_widget_available_nc25_", comment: "")))
+    }
+        
+    // NETWORKING
+    let password = CCUtility.getPassword(account.account)!
+    NKCommon.shared.setup(
+        account: account.account,
+        user: account.user,
+        userId: account.userId,
+        password: password,
+        urlBase: account.urlBase,
+        userAgent: CCUtility.getUserAgent(),
+        nextcloudVersion: 0,
+        delegate: NCNetworking.shared)
+
+    // LOG
+    let levelLog = CCUtility.getLogLevel()
+    let isSimulatorOrTestFlight = NCUtility.shared.isSimulatorOrTestFlight()
+    let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())
+
+    NKCommon.shared.levelLog = levelLog
+    if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
+        NKCommon.shared.pathLog = pathDirectoryGroup
+    }
+    if isSimulatorOrTestFlight {
+        NKCommon.shared.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) dashboard widget session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
+    } else {
+        NKCommon.shared.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) dashboard widget session with level \(levelLog) " + versionNextcloudiOS)
+    }
+    
+    let (tableDashboard, tableButton) = NCManageDatabase.shared.getDashboardWidget(account: account.account, id: id)
+    let existsButton = (tableButton?.isEmpty ?? true) ? false : true
+    let options = NKRequestOptions(timeout: 15, queue: NKCommon.shared.backgroundQueue)
+    let title = tableDashboard?.title ?? id
+
+    var imagetmp = UIImage(named: "widget")!
+    if let fileName = tableDashboard?.iconClass {
+        let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
+        if let image = UIImage(contentsOfFile: fileNamePath) {
+            imagetmp = image.withTintColor(.label, renderingMode: .alwaysOriginal)
+        }
+    }
+    let titleImage = imagetmp
+        
+    NextcloudKit.shared.getDashboardWidgetsApplication(id, options: options) { account, results, data, error in
+
+        Task {
+            var datas = [DashboardData]()
+            var numberItems = 0
+
+            if let results = results {
+                for result in results {
+                    if let items = result.items {
+                        numberItems = result.items?.count ?? 0
+                        var counter: Int = 0
+                        let dashboardItems = getDashboardItems(displaySize: displaySize, withButton: existsButton)
+                        for item in items {
+                            counter += 1
+                            let title = item.title ?? ""
+                            let subtitle = item.subtitle ?? ""
+                            var link = URL(string: "https://")!
+                            if let entryLink = item.link, let url = URL(string: entryLink) { link = url }
+                            var icon = UIImage(named: "file")!
+                            var iconFileName: String?
+                            var template: Bool = false
+                            var avatar: Bool = false
+
+                            if let iconUrl = item.iconUrl, let url = URL(string: iconUrl) {
+                                if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
+
+                                    let path = (urlComponents.path as NSString)
+                                    let pathComponents = path.components(separatedBy: "/")
+                                    let queryItems = urlComponents.queryItems
+
+                                    if (pathComponents.last as? NSString)?.pathExtension.lowercased() == "svg" {
+                                        template = true
+                                    }
+
+                                    if let item = CCUtility.value(forKey: "fileId", fromQueryItems: queryItems) {
+                                        iconFileName = item
+                                    } else if pathComponents.contains("avatar") {
+                                        iconFileName = pathComponents[pathComponents.count-2]
+                                        avatar = true
+                                    } else {
+                                        iconFileName = ((path.lastPathComponent) as NSString).deletingPathExtension
+                                    }
+                                }
+                                if let fileName = iconFileName {
+                                    let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
+                                    if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
+                                        icon = image
+                                    } else {
+                                        let (_, data, _) = await NCNetworking.shared.getPreview(url: url)
+                                        if let image = NCUtility.shared.convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName) {
+                                            icon = image
+                                        }
+                                    }
+                                }
+                            }
+
+                            let data = DashboardData(id: counter, title: title, subTitle: subtitle, link: link, icon: icon, template: template, avatar: avatar)
+                            datas.append(data)
+
+                            if datas.count == dashboardItems { break }
+                        }
+                    }
+                }
+            }
+
+            var buttons = tableButton
+            if numberItems == datas.count, let tableButton = tableButton, tableButton.contains(where: { $0.type == "more"}) {
+                buttons = tableButton.filter(({ $0.type != "more" }))
+            }
+
+            if error != .success {
+                completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: tableDashboard, buttons: buttons, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "xmark.icloud", footerText: error.errorDescription))
+            } else if datas.isEmpty {
+                completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: tableDashboard, buttons: buttons, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NSLocalizedString("_no_data_available_", comment: "")))
+            } else {
+                completion(DashboardDataEntry(date: Date(), datas: datas, dashboard: tableDashboard, buttons: buttons, isPlaceholder: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard"))
+            }
+        }
+    }
+}

+ 53 - 0
Widget/Dashboard/DashboardWidgetProvider.swift

@@ -0,0 +1,53 @@
+//
+//  DashboardWidgetProvider.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 WidgetKit
+import SwiftUI
+import Intents
+
+struct DashboardWidgetProvider: IntentTimelineProvider {
+
+    typealias Intent = DashboardIntent
+    typealias Entry = DashboardDataEntry
+
+    func placeholder(in context: Context) -> Entry {
+        let dashboardItems = getDashboardItems(displaySize: context.displaySize, withButton: false)
+        let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems])
+        let title = "Dashboard"
+        let titleImage = UIImage(named: "widget")!
+        return Entry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " widget")
+    }
+
+    func getSnapshot(for configuration: DashboardIntent, in context: Context, completion: @escaping (DashboardDataEntry) -> Void) {
+        getDashboardDataEntry(intent: configuration.Applications, isPreview: false, displaySize: context.displaySize) { entry in
+            completion(entry)
+        }
+    }
+
+    func getTimeline(for configuration: DashboardIntent, in context: Context, completion: @escaping (Timeline<DashboardDataEntry>) -> Void) {
+        getDashboardDataEntry(intent: configuration.Applications, isPreview: context.isPreview, displaySize: context.displaySize) { entry in
+            let timeLine = Timeline(entries: [entry], policy: .atEnd)
+            completion(timeLine)
+        }
+    }
+}

+ 182 - 0
Widget/Dashboard/DashboardWidgetView.swift

@@ -0,0 +1,182 @@
+//
+//  DashboardWidgetView.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 20/08/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 SwiftUI
+import WidgetKit
+
+struct DashboardWidgetView: View {
+
+    var entry: DashboardDataEntry
+    
+    var body: some View {
+        
+        GeometryReader { geo in
+
+            ZStack(alignment: .topLeading) {
+
+                HStack() {
+                    
+                    Image(uiImage: entry.titleImage)
+                        .renderingMode(.template)
+                        .resizable()
+                        .scaledToFill()
+                        .frame(width: 20, height: 20)
+                    
+                    Text(entry.title)
+                        .font(.system(size: 15))
+                        .fontWeight(.bold)
+                        .multilineTextAlignment(.center)
+                        .textCase(.uppercase)
+                        .lineLimit(1)
+                }
+                .frame(width: geo.size.width - 20)
+                .padding([.top, .leading, .trailing], 10)
+                
+                VStack(alignment: .leading) {
+                    
+                    VStack(spacing: 0) {
+                                                
+                        ForEach(entry.datas, id: \.id) { element in
+                            
+                            Link(destination: element.link) {
+                                
+                                HStack {
+                                    
+                                    let subTitleColor = Color(white: 0.5)
+
+                                    if entry.isPlaceholder {
+                                        Circle()
+                                            .fill(Color(.systemGray4))
+                                            .frame(width: 35, height: 35)
+                                    } else if element.template {
+                                        if entry.dashboard?.itemIconsRound ?? false {
+                                            Image(uiImage: element.icon)
+                                                .renderingMode(.template)
+                                                .resizable()
+                                                .scaledToFill()
+                                                .frame(width: 20, height: 20)
+                                                .foregroundColor(.white)
+                                                .padding(8
+                                                )
+                                                .background(Color(.systemGray4))
+                                                .clipShape(Circle())
+                                        } else {
+                                            Image(uiImage: element.icon)
+                                                .renderingMode(.template)
+                                                .resizable()
+                                                .scaledToFill()
+                                                .frame(width: 25, height: 25)
+                                                .clipped()
+                                                .cornerRadius(5)
+                                        }
+                                    } else {
+                                        if entry.dashboard?.itemIconsRound ?? false || element.avatar {
+                                            Image(uiImage: element.icon)
+                                                .resizable()
+                                                .scaledToFill()
+                                                .frame(width: 35, height: 35)
+                                                .clipShape(Circle())
+                                        } else {
+                                            Image(uiImage: element.icon)
+                                                .resizable()
+                                                .scaledToFill()
+                                                .frame(width: 35, height: 35)
+                                                .clipped()
+                                                .cornerRadius(5)
+                                        }
+                                    }
+
+                                    VStack(alignment: .leading, spacing: 2) {
+
+                                        Text(element.title)
+                                            .font(.system(size: 12))
+                                            .fontWeight(.regular)
+
+                                        Text(element.subTitle)
+                                            .font(.system(size: CGFloat(10)))
+                                            .foregroundColor(subTitleColor)
+                                    }
+                                    Spacer()
+                                }
+                                .padding(.leading, 10)
+                                .frame(height: 50)
+                            }
+                            Divider()
+                                .padding(.leading, 54)
+                        }
+                    }
+                }
+                .padding(.top, 35)
+                .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+
+                if let buttons = entry.buttons, !buttons.isEmpty, !entry.isPlaceholder {
+
+                    HStack(spacing: 10) {
+
+                        let brandColor = Color(NCBrandColor.shared.brand)
+                        let brandTextColor = Color(NCBrandColor.shared.brandText)
+
+                        ForEach(buttons, id: \.index) { element in
+                            Link(destination: URL(string: element.link)! , label: {
+                                
+                                Text(element.text)
+                                    .font(.system(size: 15))
+                                    .padding(7)
+                                    .background(brandColor)
+                                    .foregroundColor(brandTextColor)
+                                    .border(brandColor, width: 1)
+                                    .cornerRadius(16)
+                            })
+                        }
+                    }
+                    .frame(width: geo.size.width - 10, height: geo.size.height - 25, alignment: .bottomTrailing)
+                }
+                
+                HStack {
+
+                    Image(systemName: entry.footerImage)
+                        .resizable()
+                        .scaledToFit()
+                        .frame(width: 15, height: 15)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+
+                    Text(entry.footerText)
+                        .font(.caption2)
+                        .padding(.trailing, 13.0)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                }
+                .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
+            }
+        }
+    }
+}
+
+struct DashboardWidget_Previews: PreviewProvider {
+    static var previews: some View {
+        let datas = Array(dashboardDatasTest[0...4])
+        let title = "Dashboard"
+        let titleImage = UIImage(named: "widget")!
+        let entry = DashboardDataEntry(date: Date(), datas: datas, dashboard: nil, buttons: nil, isPlaceholder: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: "Nextcloud widget")
+        DashboardWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemLarge))
+    }
+}

+ 169 - 0
Widget/Dashboard/Intent/Base.lproj/Dashboard.intentdefinition

@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>INEnums</key>
+	<array/>
+	<key>INIntentDefinitionModelVersion</key>
+	<string>1.2</string>
+	<key>INIntentDefinitionNamespace</key>
+	<string>88xZPY</string>
+	<key>INIntentDefinitionSystemVersion</key>
+	<string>21G115</string>
+	<key>INIntentDefinitionToolsBuildVersion</key>
+	<string>14A309</string>
+	<key>INIntentDefinitionToolsVersion</key>
+	<string>14.0</string>
+	<key>INIntents</key>
+	<array>
+		<dict>
+			<key>INIntentCategory</key>
+			<string>information</string>
+			<key>INIntentDescription</key>
+			<string>Dashboard Widget</string>
+			<key>INIntentDescriptionID</key>
+			<string>tVvJ9c</string>
+			<key>INIntentEligibleForWidgets</key>
+			<true/>
+			<key>INIntentIneligibleForSuggestions</key>
+			<true/>
+			<key>INIntentLastParameterTag</key>
+			<integer>7</integer>
+			<key>INIntentName</key>
+			<string>Dashboard</string>
+			<key>INIntentParameters</key>
+			<array>
+				<dict>
+					<key>INIntentParameterConfigurable</key>
+					<true/>
+					<key>INIntentParameterDisplayName</key>
+					<string>Widget</string>
+					<key>INIntentParameterDisplayNameID</key>
+					<string>TRaTZg</string>
+					<key>INIntentParameterDisplayPriority</key>
+					<integer>1</integer>
+					<key>INIntentParameterName</key>
+					<string>Applications</string>
+					<key>INIntentParameterObjectType</key>
+					<string>Applications</string>
+					<key>INIntentParameterObjectTypeNamespace</key>
+					<string>88xZPY</string>
+					<key>INIntentParameterPromptDialogs</key>
+					<array>
+						<dict>
+							<key>INIntentParameterPromptDialogCustom</key>
+							<true/>
+							<key>INIntentParameterPromptDialogType</key>
+							<string>Configuration</string>
+						</dict>
+						<dict>
+							<key>INIntentParameterPromptDialogCustom</key>
+							<true/>
+							<key>INIntentParameterPromptDialogType</key>
+							<string>Primary</string>
+						</dict>
+					</array>
+					<key>INIntentParameterSupportsDynamicEnumeration</key>
+					<true/>
+					<key>INIntentParameterTag</key>
+					<integer>7</integer>
+					<key>INIntentParameterType</key>
+					<string>Object</string>
+				</dict>
+			</array>
+			<key>INIntentResponse</key>
+			<dict>
+				<key>INIntentResponseCodes</key>
+				<array>
+					<dict>
+						<key>INIntentResponseCodeName</key>
+						<string>success</string>
+						<key>INIntentResponseCodeSuccess</key>
+						<true/>
+					</dict>
+					<dict>
+						<key>INIntentResponseCodeName</key>
+						<string>failure</string>
+					</dict>
+				</array>
+				<key>INIntentResponseLastParameterTag</key>
+				<integer>1</integer>
+			</dict>
+			<key>INIntentTitle</key>
+			<string>Dashboard</string>
+			<key>INIntentTitleID</key>
+			<string>gpCwrM</string>
+			<key>INIntentType</key>
+			<string>Custom</string>
+			<key>INIntentVerb</key>
+			<string>View</string>
+		</dict>
+	</array>
+	<key>INTypes</key>
+	<array>
+		<dict>
+			<key>INTypeDisplayName</key>
+			<string>Applications</string>
+			<key>INTypeDisplayNameID</key>
+			<string>l090JH</string>
+			<key>INTypeLastPropertyTag</key>
+			<integer>99</integer>
+			<key>INTypeName</key>
+			<string>Applications</string>
+			<key>INTypeProperties</key>
+			<array>
+				<dict>
+					<key>INTypePropertyDefault</key>
+					<true/>
+					<key>INTypePropertyDisplayPriority</key>
+					<integer>1</integer>
+					<key>INTypePropertyName</key>
+					<string>identifier</string>
+					<key>INTypePropertyTag</key>
+					<integer>1</integer>
+					<key>INTypePropertyType</key>
+					<string>String</string>
+				</dict>
+				<dict>
+					<key>INTypePropertyDefault</key>
+					<true/>
+					<key>INTypePropertyDisplayPriority</key>
+					<integer>2</integer>
+					<key>INTypePropertyName</key>
+					<string>displayString</string>
+					<key>INTypePropertyTag</key>
+					<integer>2</integer>
+					<key>INTypePropertyType</key>
+					<string>String</string>
+				</dict>
+				<dict>
+					<key>INTypePropertyDefault</key>
+					<true/>
+					<key>INTypePropertyDisplayPriority</key>
+					<integer>3</integer>
+					<key>INTypePropertyName</key>
+					<string>pronunciationHint</string>
+					<key>INTypePropertyTag</key>
+					<integer>3</integer>
+					<key>INTypePropertyType</key>
+					<string>String</string>
+				</dict>
+				<dict>
+					<key>INTypePropertyDefault</key>
+					<true/>
+					<key>INTypePropertyDisplayPriority</key>
+					<integer>4</integer>
+					<key>INTypePropertyName</key>
+					<string>alternativeSpeakableMatches</string>
+					<key>INTypePropertySupportsMultipleValues</key>
+					<true/>
+					<key>INTypePropertyTag</key>
+					<integer>4</integer>
+					<key>INTypePropertyType</key>
+					<string>SpeakableString</string>
+				</dict>
+			</array>
+		</dict>
+	</array>
+</dict>
+</plist>

+ 8 - 0
Widget/Dashboard/Intent/ca.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/cs-CZ.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/da.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/de.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/en-GB.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/en.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-419.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-CL.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-CO.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-CR.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-DO.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-EC.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-GT.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-HN.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-MX.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-NI.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-PA.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-PE.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-PR.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-PY.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-SV.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es-UY.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/es.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/eu.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/fr.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/gl.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/hu.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/is.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/it.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/ja-JP.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/ka-GE.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/ko.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/nb-NO.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/nl.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/pl.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/pt-BR.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/pt-PT.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/ru.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/sk-SK.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/sr.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/sv.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/tr.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/zh-Hans.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 8 - 0
Widget/Dashboard/Intent/zh-Hant-TW.lproj/Dashboard.strings

@@ -0,0 +1,8 @@
+"TRaTZg" = "Widget";
+
+"gpCwrM" = "Dashboard";
+
+"l090JH" = "Applications";
+
+"tVvJ9c" = "Dashboard Widget";
+

+ 272 - 0
Widget/Files/FilesData.swift

@@ -0,0 +1,272 @@
+//
+//  FilesData.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 WidgetKit
+import NextcloudKit
+
+struct FilesDataEntry: TimelineEntry {
+    let date: Date
+    let datas: [FilesData]
+    let isPlaceholder: Bool
+    let tile: String
+    let footerImage: String
+    let footerText: String
+}
+
+struct FilesData: Identifiable, Hashable {
+    var id: String
+    var image: UIImage
+    var title: String
+    var subTitle: String
+    var url: URL
+}
+
+let filesDatasTest: [FilesData] = [
+    .init(id: "0", image: UIImage(named: "widget")!, title: "title1", subTitle: "subTitle-description1", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "1", image: UIImage(named: "widget")!, title: "title2", subTitle: "subTitle-description2", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "2", image: UIImage(named: "widget")!, title: "title3", subTitle: "subTitle-description3", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "3", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "4", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "5", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "6", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "7", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "8", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!),
+    .init(id: "9", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!)
+]
+
+func getTitleFilesWidget() -> String {
+
+    let hour = Calendar.current.component(.hour, from: Date())
+    var good = ""
+
+    switch hour {
+    case 6..<12: good = NSLocalizedString("_good_morning_", value: "Good morning", comment: "")
+    case 12: good = NSLocalizedString("_good_day_", value: "Good day", comment: "")
+    case 13..<17: good = NSLocalizedString("_good_afternoon_", value: "Good afternoon", comment: "")
+    case 17..<22: good = NSLocalizedString("_good_evening_", value: "Good evening", comment: "")
+    default: good = NSLocalizedString("_good_night_", value: "Good night", comment: "")
+    }
+
+    if let account = NCManageDatabase.shared.getActiveAccount() {
+        return good + ", " + account.displayName
+    } else {
+        return good
+    }
+}
+
+func getFilesItems(displaySize: CGSize) -> Int {
+    
+    let height = Int((displaySize.height - 100) / 50)
+    return height
+}
+
+func getFilesDataEntry(isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: FilesDataEntry) -> Void) {
+
+    let filesItems = getFilesItems(displaySize: displaySize)
+    let datasPlaceholder = Array(filesDatasTest[0...filesItems - 1])
+    let title = getTitleFilesWidget()
+    
+    
+    if isPreview {
+        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, tile: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files"))
+    }
+
+    guard let account = NCManageDatabase.shared.getActiveAccount() else {
+        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, tile: title, footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
+    }
+
+    @Sendable func isLive(file: NKFile, files: [NKFile]) -> Bool {
+
+        if file.ext.lowercased() != "mov" { return false }
+        if files.filter({ ($0.fileNameWithoutExt == file.fileNameWithoutExt) && ($0.ext.lowercased() == "jpg") }).first != nil {
+            return true
+        }
+        return false
+    }
+
+    // NETWORKING
+    let password = CCUtility.getPassword(account.account)!
+    NKCommon.shared.setup(
+        account: account.account,
+        user: account.user,
+        userId: account.userId,
+        password: password,
+        urlBase: account.urlBase,
+        userAgent: CCUtility.getUserAgent(),
+        nextcloudVersion: 0,
+        delegate: NCNetworking.shared)
+
+    let requestBodyRecent =
+    """
+    <?xml version=\"1.0\"?>
+    <d:searchrequest xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">
+    <d:basicsearch>
+        <d:select>
+            <d:prop>
+                <d:displayname/>
+                <d:getcontenttype/>
+                <d:resourcetype/>
+                <d:getcontentlength/>
+                <d:getlastmodified/>
+                <d:getetag/>
+                <d:quota-used-bytes/>
+                <d:quota-available-bytes/>
+                <permissions xmlns=\"http://owncloud.org/ns\"/>
+                <id xmlns=\"http://owncloud.org/ns\"/>
+                <fileid xmlns=\"http://owncloud.org/ns\"/>
+                <size xmlns=\"http://owncloud.org/ns\"/>
+                <favorite xmlns=\"http://owncloud.org/ns\"/>
+                <creation_time xmlns=\"http://nextcloud.org/ns\"/>
+                <upload_time xmlns=\"http://nextcloud.org/ns\"/>
+                <is-encrypted xmlns=\"http://nextcloud.org/ns\"/>
+                <mount-type xmlns=\"http://nextcloud.org/ns\"/>
+                <owner-id xmlns=\"http://owncloud.org/ns\"/>
+                <owner-display-name xmlns=\"http://owncloud.org/ns\"/>
+                <comments-unread xmlns=\"http://owncloud.org/ns\"/>
+                <has-preview xmlns=\"http://nextcloud.org/ns\"/>
+                <trashbin-filename xmlns=\"http://nextcloud.org/ns\"/>
+                <trashbin-original-location xmlns=\"http://nextcloud.org/ns\"/>
+                <trashbin-deletion-time xmlns=\"http://nextcloud.org/ns\"/>
+            </d:prop>
+        </d:select>
+    <d:from>
+        <d:scope>
+            <d:href>%@</d:href>
+            <d:depth>infinity</d:depth>
+        </d:scope>
+    </d:from>
+    <d:where>
+        <d:lt>
+            <d:prop>
+                <d:getlastmodified/>
+            </d:prop>
+            <d:literal>%@</d:literal>
+        </d:lt>
+    </d:where>
+    <d:orderby>
+        <d:order>
+            <d:prop>
+                <d:getlastmodified/>
+            </d:prop>
+            <d:descending/>
+        </d:order>
+    </d:orderby>
+    <d:limit>
+        <d:nresults>50</d:nresults>
+    </d:limit>
+    </d:basicsearch>
+    </d:searchrequest>
+    """
+
+    let dateFormatter = DateFormatter()
+    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
+    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
+    let lessDateString = dateFormatter.string(from: Date())
+    let requestBody = String(format: requestBodyRecent, "/files/" + account.userId, lessDateString)
+
+    // LOG
+    let levelLog = CCUtility.getLogLevel()
+    let isSimulatorOrTestFlight = NCUtility.shared.isSimulatorOrTestFlight()
+    let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())
+
+    NKCommon.shared.levelLog = levelLog
+    if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
+        NKCommon.shared.pathLog = pathDirectoryGroup
+    }
+    if isSimulatorOrTestFlight {
+        NKCommon.shared.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) widget session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
+    } else {
+        NKCommon.shared.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) widget session with level \(levelLog) " + versionNextcloudiOS)
+    }
+    
+    let options = NKRequestOptions(timeout: 15)
+    NextcloudKit.shared.searchBodyRequest(serverUrl: account.urlBase, requestBody: requestBody, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { _, files, data, error in
+        Task {
+            var datas: [FilesData] = []
+            var imageRecent = UIImage(named: "file")!
+
+            for file in files {
+                guard !file.directory else { continue }
+                guard !isLive(file: file, files: files) else { continue }
+
+                // SUBTITLE
+                let subTitle = CCUtility.dateDiff(file.date as Date) + " · " + CCUtility.transformedSize(file.size)
+
+                // URL: nextcloud://open-file?path=Talk/IMG_0000123.jpg&user=marinofaggiana&link=https://cloud.nextcloud.com/f/123
+                guard var path = NCUtilityFileSystem.shared.getPath(path: file.path, user: file.user, fileName: file.fileName).urlEncoded else { continue }
+                if path.first == "/" { path = String(path.dropFirst())}
+                guard let user = file.user.urlEncoded else { continue }
+                let link = file.urlBase + "/f/" + file.fileId
+                let urlString = "nextcloud://open-file?path=\(path)&user=\(user)&link=\(link)"
+                guard let url = URL(string: urlString) else { continue }
+
+                // IMAGE
+                if !file.iconName.isEmpty {
+                    imageRecent = UIImage(named: file.iconName)!
+                }
+                if let image = NCUtility.shared.createFilePreviewImage(ocId: file.ocId, etag: file.etag, fileNameView: file.fileName, classFile: file.classFile, status: 0, createPreviewMedia: false) {
+                    imageRecent = image
+                } else if file.hasPreview {
+                    let fileNamePathOrFileId = CCUtility.returnFileNamePath(fromFileName: file.fileName, serverUrl: file.serverUrl, urlBase: file.urlBase, account: account.account)!
+                    let fileNamePreviewLocalPath = CCUtility.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)!
+                    let fileNameIconLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)!
+                    let (_, _, imageIcon, _, _, _) = await NCNetworking.shared.downloadPreview(fileNamePathOrFileId: fileNamePathOrFileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, widthPreview: NCGlobal.shared.sizePreview, heightPreview: NCGlobal.shared.sizePreview, fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon)
+                    if let image = imageIcon {
+                        imageRecent = image
+                    }
+                    /*
+                    do {
+                        let fileNamePathOrFileId = CCUtility.returnFileNamePath(fromFileName: file.fileName, serverUrl: file.serverUrl, urlBase: file.urlBase, account: account.account)!
+                        let fileNamePreviewLocalPath = CCUtility.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)!
+                        let fileNameIconLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)!
+                        let (_, _, imageIcon, _, _) = try await NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePathOrFileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, widthPreview: NCGlobal.shared.sizePreview, heightPreview: NCGlobal.shared.sizePreview, fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon)
+                        if let image = imageIcon {
+                            imageRecent = image
+                        }
+                    } catch {
+                        print(error)
+                    }
+                    */
+                }
+
+                // DATA
+                let data = FilesData.init(id: file.ocId, image: imageRecent, title: file.fileName, subTitle: subTitle, url: url)
+                datas.append(data)
+                if datas.count == filesItems { break}
+            }
+
+            if error != .success {
+                completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, tile: title, footerImage: "xmark.icloud", footerText: error.errorDescription))
+            } else if datas.isEmpty {
+                var footerText = NSLocalizedString("_no_data_available_", comment: "")
+                let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
+                if serverVersionMajor < NCGlobal.shared.nextcloudVersion25 {
+                    footerText = NSLocalizedString("_widget_available_nc25_", comment: "")
+                }
+                completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, tile: title, footerImage: "checkmark.icloud", footerText: footerText))
+            } else {
+                completion(FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, tile: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files"))
+            }
+        }
+    }
+}

+ 50 - 0
Widget/Files/FilesWidgetProvider.swift

@@ -0,0 +1,50 @@
+//
+//  FilesWidgetProvider.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 WidgetKit
+import SwiftUI
+
+struct FilesWidgetProvider: TimelineProvider {
+
+    typealias Entry = FilesDataEntry
+
+    func placeholder(in context: Context) -> Entry {
+        let filesItems = getFilesItems(displaySize: context.displaySize)
+        let datasPlaceholder = Array(filesDatasTest[0...filesItems - 1])
+        let title = getTitleFilesWidget()
+        return Entry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, tile: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files")
+    }
+
+    func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
+        getFilesDataEntry(isPreview: false, displaySize: context.displaySize) { entry in
+            completion(entry)
+        }
+    }
+
+    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
+        getFilesDataEntry(isPreview: context.isPreview, displaySize: context.displaySize) { entry in
+            let timeLine = Timeline(entries: [entry], policy: .atEnd)
+            completion(timeLine)
+        }
+    }
+}

+ 169 - 0
Widget/Files/FilesWidgetView.swift

@@ -0,0 +1,169 @@
+//
+//  FilesWidgetView.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 SwiftUI
+import WidgetKit
+
+struct FilesWidgetView: View {
+    
+    var entry: FilesDataEntry
+    
+    var body: some View {
+        
+        GeometryReader { geo in
+            
+            ZStack(alignment: .topLeading) {
+                
+                HStack() {
+                    
+                    Text(entry.tile)
+                        .font(.system(size: 12))
+                        .fontWeight(.bold)
+                        .multilineTextAlignment(.center)
+                        .textCase(.uppercase)
+                        .lineLimit(1)
+                }
+                .frame(width: geo.size.width - 20)
+                .padding([.top, .leading, .trailing], 10)
+                
+                VStack(alignment: .leading) {
+                    
+                    VStack(spacing: 0) {
+                        
+                        ForEach(entry.datas, id: \.id) { element in
+                            
+                            Link(destination: element.url) {
+                                
+                                HStack {
+
+                                    Image(uiImage: element.image)
+                                        .resizable()
+                                        .scaledToFill()
+                                        .frame(width: 35, height: 35)
+                                        .clipped()
+                                        .cornerRadius(5)
+                                    
+                                    VStack(alignment: .leading, spacing: 2) {
+                                        
+                                        Text(element.title)
+                                            .font(.system(size: 12))
+                                            .fontWeight(.regular)
+                                        
+                                        Text(element.subTitle)
+                                            .font(.system(size: CGFloat(10)))
+                                            .foregroundColor(Color(.systemGray))
+                                    }
+                                    Spacer()
+                                }
+                                .padding(.leading, 10)
+                                .frame(height: 50)
+                            }
+                            Divider()
+                                .padding(.leading, 54)
+                        }
+                    }
+                }
+                .padding(.top, 30)
+                .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+
+                HStack(spacing: 0) {
+
+                    let sizeButton: CGFloat = 40
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionUploadAsset, label: {
+                        Image("addImage")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding(11)
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionScanDocument, label: {
+                        Image("scan")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding(11)
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionTextDocument, label: {
+                        Image("note.text")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding(11)
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionVoiceMemo, label: {
+                        Image("microphone")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding(11)
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+                }
+                .frame(width: geo.size.width, height: geo.size.height - 25, alignment: .bottomTrailing)
+                .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+
+                HStack {
+
+                    Image(systemName: entry.footerImage)
+                        .resizable()
+                        .scaledToFit()
+                        .frame(width: 15, height: 15)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                
+                    Text(entry.footerText)
+                        .font(.caption2)
+                        .padding(.trailing, 13.0)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                }
+                .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
+            }
+        }
+    }
+}
+
+struct FilesWidget_Previews: PreviewProvider {
+    static var previews: some View {
+        let datas = Array(filesDatasTest[0...4])
+        let entry = FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, tile: "Good afternoon, Marino Faggiana", footerImage: "checkmark.icloud", footerText: "Nextcloud files")
+        FilesWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemLarge))
+    }
+}

+ 95 - 0
Widget/Lockscreen/LockscreenData.swift

@@ -0,0 +1,95 @@
+//
+//  LockscreenData.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 13/10/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 WidgetKit
+import NextcloudKit
+
+struct LockscreenData: TimelineEntry {
+    let date: Date
+    let isPlaceholder: Bool
+    let activity: String
+    let link: URL
+    let quotaRelative: Float
+    let quotaUsed: String
+    let quotaTotal: String
+}
+
+func getLockscreenDataEntry(isPreview: Bool, completion: @escaping (_ entry: LockscreenData) -> Void) {
+
+    if isPreview {
+        return completion(LockscreenData(date: Date(), isPlaceholder: true, activity: "", link: URL(string: "https://")!, quotaRelative: 0, quotaUsed: "", quotaTotal: ""))
+    }
+
+    guard let account = NCManageDatabase.shared.getActiveAccount() else {
+        return completion(LockscreenData(date: Date(), isPlaceholder: true, activity: "", link: URL(string: "https://")!, quotaRelative: 0, quotaUsed: "", quotaTotal: ""))
+    }
+
+    var quotaRelative: Float = 0
+    if account.quotaRelative > 0 {
+        quotaRelative = Float(account.quotaRelative) / 100
+    }
+    let quotaUsed: String = CCUtility.transformedSize(account.quotaUsed)
+    var quotaTotal: String = ""
+
+    switch account.quotaTotal {
+    case -1:
+        quotaTotal = ""
+    case -2:
+        quotaTotal = ""
+    case -3:
+        quotaTotal = ""
+    default:
+        quotaTotal = CCUtility.transformedSize(account.quotaTotal)
+    }
+
+    let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
+    if serverVersionMajor >= NCGlobal.shared.nextcloudVersion25 {
+
+        // NETWORKING
+        let password = CCUtility.getPassword(account.account)!
+        NKCommon.shared.setup(
+            account: account.account,
+            user: account.user,
+            userId: account.userId,
+            password: password,
+            urlBase: account.urlBase,
+            userAgent: CCUtility.getUserAgent(),
+            nextcloudVersion: 0,
+            delegate: NCNetworking.shared)
+
+        let options = NKRequestOptions(timeout: 15, queue: NKCommon.shared.backgroundQueue)
+        NextcloudKit.shared.getDashboardWidgetsApplication("activity", options: options) { _, results, _, error in
+            var activity: String = NSLocalizedString("_no_data_available_", comment: "")
+            var link = URL(string: "https://")!
+            if error == .success, let result = results?.first {
+                if let item = result.items?.first {
+                    if let title = item.title {  activity = title }
+                    if let itemLink = item.link, let url = URL(string: itemLink) { link = url }
+                }
+            }
+            completion(LockscreenData(date: Date(), isPlaceholder: false, activity: activity, link: link, quotaRelative: quotaRelative, quotaUsed: quotaUsed, quotaTotal: quotaTotal))
+        }
+    } else {
+        completion(LockscreenData(date: Date(), isPlaceholder: false, activity: NSLocalizedString("_widget_available_nc25_", comment: ""), link: URL(string: "https://")!, quotaRelative: quotaRelative, quotaUsed: quotaUsed, quotaTotal: quotaTotal))
+    }
+}

+ 47 - 0
Widget/Lockscreen/LockscreenWidgetProvider.swift

@@ -0,0 +1,47 @@
+//
+//  LockscreenWidgetProvider.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 13/10/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 WidgetKit
+import SwiftUI
+
+struct LockscreenWidgetProvider: TimelineProvider {
+
+    typealias Entry = LockscreenData
+
+    func placeholder(in context: Context) -> Entry {
+        return Entry(date: Date(), isPlaceholder: true, activity: "", link: URL(string: "https://")!, quotaRelative: 0, quotaUsed: "", quotaTotal: "")
+    }
+
+    func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
+        getLockscreenDataEntry(isPreview: false) { entry in
+            completion(entry)
+        }
+    }
+
+    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
+        getLockscreenDataEntry(isPreview: context.isPreview) { entry in
+            let timeLine = Timeline(entries: [entry], policy: .atEnd)
+            completion(timeLine)
+        }
+    }
+}

+ 75 - 0
Widget/Lockscreen/LockscreenWidgetView.swift

@@ -0,0 +1,75 @@
+//
+//  LockscreenWidgetView.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 13/10/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 SwiftUI
+import WidgetKit
+
+@available(iOSApplicationExtension 16.0, *)
+struct LockscreenWidgetView: View {
+
+    let entry: LockscreenData
+    @Environment(\.widgetFamily) private var family
+
+    var body: some View {
+        switch family {
+        case .accessoryCircular:
+            Gauge(
+                value: entry.quotaRelative,
+                label: { Text("  " + entry.quotaTotal + "  ") },
+                currentValueLabel: { Text(entry.quotaUsed) }
+            )
+            .gaugeStyle(.accessoryCircular)
+            .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+        case .accessoryRectangular:
+            VStack(alignment: .leading, spacing: 1) {
+                HStack(spacing: 1) {
+                    Image("activity")
+                        .renderingMode(.template)
+                        .resizable()
+                        .scaledToFill()
+                        .foregroundColor(.gray)
+                        .frame(width: 11, height: 11)
+                    Text(NSLocalizedString("_recent_activity_", comment: ""))
+                        .font(.system(size: 11))
+                        .fontWeight(.heavy)
+                        .foregroundColor(.gray)
+                }
+                Text(entry.activity)
+                    .font(.system(size: 12)).bold()
+            }
+            .widgetURL(entry.link)
+            .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+        default:
+            Text("Not implemented")
+        }
+    }
+}
+
+@available(iOSApplicationExtension 16.0, *)
+struct LockscreenWidgetView_Previews: PreviewProvider {
+    static var previews: some View {
+        let entry = LockscreenData(date: Date(), isPlaceholder: false, activity: "Alba Mayoral changed Marketing / Regional Marketing / Agenda Meetings / Q4 2022 / OCTOBER / 13.11 Afrah Kahlid.md", link: URL(string: "https://")!, quotaRelative: 0.5, quotaUsed: "22 GB", quotaTotal: "50 GB")
+        LockscreenWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .accessoryRectangular)).previewDisplayName("Rectangular")
+        LockscreenWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .accessoryCircular)).previewDisplayName("Circular")
+    }
+}

+ 44 - 0
Widget/Toolbar/ToolbarData.swift

@@ -0,0 +1,44 @@
+//
+//  ToolbarData.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 WidgetKit
+
+struct ToolbarDataEntry: TimelineEntry {
+    let date: Date
+    let isPlaceholder: Bool
+    let footerImage: String
+    let footerText: String
+}
+
+func getToolbarDataEntry(isPreview: Bool, completion: @escaping (_ entry: ToolbarDataEntry) -> Void) {
+
+    if isPreview {
+        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
+    }
+
+    if NCManageDatabase.shared.getActiveAccount() == nil {
+        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
+    }
+
+    completion(ToolbarDataEntry(date: Date(), isPlaceholder: false, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
+}

+ 47 - 0
Widget/Toolbar/ToolbarWidgetProvider.swift

@@ -0,0 +1,47 @@
+//
+//  ToolbarWidgetProvider.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 WidgetKit
+import SwiftUI
+
+struct ToolbarWidgetProvider: TimelineProvider {
+
+    typealias Entry = ToolbarDataEntry
+
+    func placeholder(in context: Context) -> Entry {
+        return Entry(date: Date(), isPlaceholder: true, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
+    }
+
+    func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
+        getToolbarDataEntry(isPreview: false) { entry in
+            completion(entry)
+        }
+    }
+
+    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
+        getToolbarDataEntry(isPreview: context.isPreview) { entry in
+            let timeLine = Timeline(entries: [entry], policy: .atEnd)
+            completion(timeLine)
+        }
+    }
+}

+ 118 - 0
Widget/Toolbar/ToolbarWidgetView.swift

@@ -0,0 +1,118 @@
+//
+//  ToolbarWidgetView.swift
+//  Widget
+//
+//  Created by Marino Faggiana on 25/08/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 SwiftUI
+import WidgetKit
+
+struct ToolbarWidgetView: View {
+
+    var entry: ToolbarDataEntry
+
+    var body: some View {
+
+        GeometryReader { geo in
+
+            ZStack(alignment: .topLeading) {
+
+                Color(.black).opacity(0.9)
+                    .ignoresSafeArea()
+
+                HStack(spacing: 0) {
+
+                    let sizeButton: CGFloat = 65
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionUploadAsset, label: {
+                        Image("addImage")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding()
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionScanDocument, label: {
+                        Image("scan")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding()
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionTextDocument, label: {
+                        Image("note.text")
+                            .resizable()
+                            .renderingMode(.template)
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding()
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+
+                    Link(destination: entry.isPlaceholder ? NCGlobal.shared.widgetActionNoAction : NCGlobal.shared.widgetActionVoiceMemo, label: {
+                        Image("microphone")
+                            .resizable()
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .padding()
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .clipShape(Circle())
+                            .scaledToFit()
+                            .frame(width: geo.size.width / 4, height: sizeButton)
+                    })
+                }
+                .frame(width: geo.size.width, height: geo.size.height, alignment: .center)
+                .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+
+                HStack {
+
+                    Image(systemName: entry.footerImage)
+                        .resizable()
+                        .scaledToFit()
+                        .frame(width: 15, height: 15)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+
+                    Text(entry.footerText)
+                        .font(.caption2)
+                        .padding(.trailing, 13.0)
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                }
+                .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
+            }
+        }
+    }
+}
+
+struct ToolbarWidget_Previews: PreviewProvider {
+    static var previews: some View {
+        let entry = ToolbarDataEntry(date: Date(), isPlaceholder: false, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
+        ToolbarWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemMedium))
+    }
+}

+ 5 - 0
Widget/Widget-Brinding-header.h

@@ -0,0 +1,5 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "CCUtility.h"

+ 93 - 0
Widget/Widget.swift

@@ -0,0 +1,93 @@
+//
+//  NextcloudWidget.swift
+//  NextcloudWidget
+//
+//  Created by Marino Faggiana on 20/08/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 WidgetKit
+import SwiftUI
+
+@main
+struct NextcloudWidgetBundle: WidgetBundle {
+
+    @WidgetBundleBuilder
+    var body: some Widget {
+        DashboardWidget()
+        FilesWidget()
+        ToolbarWidget()
+        LockscreenWidget()
+    }
+}
+
+struct DashboardWidget: Widget {
+    let kind: String = "DashboardWidget"
+
+    var body: some WidgetConfiguration {
+        IntentConfiguration(kind: kind, intent: DashboardIntent.self, provider: DashboardWidgetProvider()) { entry in
+            DashboardWidgetView(entry: entry)
+        }
+        .supportedFamilies([.systemLarge])
+        .configurationDisplayName("Dashboard")
+        .description(NSLocalizedString("_description_dashboardwidget_", comment: ""))
+    }
+}
+
+struct FilesWidget: Widget {
+    let kind: String = "FilesWidget"
+
+    var body: some WidgetConfiguration {
+        StaticConfiguration(kind: kind, provider: FilesWidgetProvider()) { entry in
+            FilesWidgetView(entry: entry)
+        }
+        .supportedFamilies([.systemLarge])
+        .configurationDisplayName("Files")
+        .description(NSLocalizedString("_description_fileswidget_", comment: ""))
+    }
+}
+
+struct ToolbarWidget: Widget {
+    let kind: String = "ToolbarWidget"
+
+    var body: some WidgetConfiguration {
+        StaticConfiguration(kind: kind, provider: ToolbarWidgetProvider()) { entry in
+            ToolbarWidgetView(entry: entry)
+        }
+        .supportedFamilies([.systemMedium])
+        .configurationDisplayName("Toolbar")
+        .description(NSLocalizedString("_description_toolbarwidget_", comment: ""))
+    }
+}
+
+struct LockscreenWidget: Widget {
+    let kind: String = "LockscreenWidget"
+
+    var body: some WidgetConfiguration {
+        if #available(iOSApplicationExtension 16.0, *) {
+            return StaticConfiguration(kind: kind, provider: LockscreenWidgetProvider()) { entry in
+                LockscreenWidgetView(entry: entry)
+            }
+            .supportedFamilies([.accessoryRectangular, .accessoryCircular])
+            .configurationDisplayName(NSLocalizedString("_title_lockscreenwidget_", comment: ""))
+            .description(NSLocalizedString("_description_lockscreenwidget_", comment: ""))
+        } else {
+            return EmptyWidgetConfiguration()
+        }
+    }
+}

+ 42 - 0
WidgetDashboardIntentHandler/IntentHandler.swift

@@ -0,0 +1,42 @@
+//
+//  IntentHandler.swift
+//  WidgetDashboardIntentHandler
+//
+//  Created by Marino Faggiana on 08/10/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+
+import Intents
+import RealmSwift
+
+class IntentHandler: INExtension, DashboardIntentHandling {
+
+    func provideApplicationsOptionsCollection(for intent: DashboardIntent, with completion: @escaping (INObjectCollection<Applications>?, Error?) -> Void) {
+
+        var applications: [Applications] = []
+
+        guard let account = NCManageDatabase.shared.getActiveAccount() else {
+            completion(nil, nil)
+            return
+        }
+
+        let results = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account)
+        for result in results {
+            let application = Applications(identifier: result.id, display: result.title)
+            applications.append(application)
+        }
+
+        completion(INObjectCollection(items: applications), nil)
+    }
+
+    func defaultApplications(for intent: DashboardIntent) -> Applications? {
+
+        guard let account = NCManageDatabase.shared.getActiveAccount() else {
+            return nil
+        }
+        if let result = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account).first {
+            return Applications(identifier: result.id, display: result.title)
+        }
+        return nil
+    }
+}

+ 5 - 0
WidgetDashboardIntentHandler/WidgetDashboardIntentHandler-Brinding-header.h

@@ -0,0 +1,5 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "CCUtility.h"

+ 7 - 6
iOSClient/.tx/config

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

+ 4 - 4
iOSClient/Account Request/NCAccountRequest.swift

@@ -22,7 +22,7 @@
 //
 
 import UIKit
-import NCCommunication
+import NextcloudKit
 
 public protocol NCAccountRequestDelegate: AnyObject {
     func accountRequestAddAccount()
@@ -61,13 +61,13 @@ class NCAccountRequest: UIViewController {
 
         titleLabel.text = NSLocalizedString("_account_select_", comment: "")
 
-        closeButton.setImage(NCUtility.shared.loadImage(named: "xmark", color: NCBrandColor.shared.label), for: .normal)
+        closeButton.setImage(NCUtility.shared.loadImage(named: "xmark", color: .label), for: .normal)
 
         tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1))
         tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
 
-        view.backgroundColor = NCBrandColor.shared.secondarySystemBackground
-        tableView.backgroundColor = NCBrandColor.shared.secondarySystemBackground
+        view.backgroundColor = .secondarySystemBackground
+        tableView.backgroundColor = .secondarySystemBackground
 
         progressView.trackTintColor = .clear
         progressView.progress = 1

+ 36 - 32
iOSClient/Activity/NCActivity.swift

@@ -24,7 +24,7 @@
 
 import UIKit
 import SwiftRichString
-import NCCommunication
+import NextcloudKit
 
 class NCActivity: UIViewController, NCSharePagingContent {
 
@@ -60,13 +60,13 @@ class NCActivity: UIViewController, NCSharePagingContent {
         super.viewDidLoad()
 
         self.navigationController?.navigationBar.prefersLargeTitles = true
-        view.backgroundColor = NCBrandColor.shared.systemBackground
+        view.backgroundColor = .systemBackground
         self.title = NSLocalizedString("_activity_", comment: "")
 
         tableView.allowsSelection = false
         tableView.separatorColor = UIColor.clear
         tableView.contentInset = insets
-        tableView.backgroundColor = NCBrandColor.shared.systemBackground
+        tableView.backgroundColor = .systemBackground
 
         if showComments {
             setupComments()
@@ -83,12 +83,12 @@ class NCActivity: UIViewController, NCSharePagingContent {
         commentView = Bundle.main.loadNibNamed("NCActivityCommentView", owner: self, options: nil)?.first as? NCActivityCommentView
         commentView?.setup(urlBase: appDelegate, account: activeAccount) { newComment in
             guard let newComment = newComment, !newComment.isEmpty, let metadata = self.metadata else { return }
-            NCCommunication.shared.putComments(fileId: metadata.fileId, message: newComment) { _, errorCode, errorDescription in
-                if errorCode == 0 {
+            NextcloudKit.shared.putComments(fileId: metadata.fileId, message: newComment) { _, error in
+                if error == .success {
                     self.commentView?.newCommentField.text?.removeAll()
                     self.loadComments()
                 } else {
-                    NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                    NCContentPresenter.shared.showError(error: error)
                 }
             }
         }
@@ -96,7 +96,11 @@ class NCActivity: UIViewController, NCSharePagingContent {
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
+
         appDelegate.activeViewController = self
+
+        navigationController?.setFileAppreance()
+
         NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
         initialize()
     }
@@ -163,7 +167,7 @@ extension NCActivity: UITableViewDelegate {
 
         let label = UILabel()
         label.font = UIFont.boldSystemFont(ofSize: 13)
-        label.textColor = NCBrandColor.shared.label
+        label.textColor = .label
         label.text = CCUtility.getTitleSectionDate(sectionDates[section])
         label.textAlignment = .center
         label.layer.cornerRadius = 11
@@ -218,13 +222,13 @@ extension NCActivity: UITableViewDataSource {
         NCOperationQueue.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
         // Username
         cell.labelUser.text = comment.actorDisplayName
-        cell.labelUser.textColor = NCBrandColor.shared.label
+        cell.labelUser.textColor = .label
         // Date
         cell.labelDate.text = CCUtility.dateDiff(comment.creationDateTime as Date)
-        cell.labelDate.textColor = NCBrandColor.shared.systemGray4
+        cell.labelDate.textColor = .systemGray4
         // Message
         cell.labelMessage.text = comment.message
-        cell.labelMessage.textColor = NCBrandColor.shared.label
+        cell.labelMessage.textColor = .label
         // Button Menu
         if comment.actorId == appDelegate.userId {
             cell.buttonMenu.isHidden = false
@@ -248,7 +252,7 @@ extension NCActivity: UITableViewDataSource {
         cell.avatar.isHidden = true
         cell.subjectTrailingConstraint.constant = 10
         cell.didSelectItemEnable = self.didSelectItemEnable
-        cell.subject.textColor = NCBrandColor.shared.label
+        cell.subject.textColor = .label
         cell.viewController = self
 
         // icon
@@ -262,8 +266,8 @@ extension NCActivity: UITableViewDataSource {
                     cell.icon.image = image
                 }
             } else {
-                NCCommunication.shared.downloadContent(serverUrl: activity.icon) { _, data, errorCode, _ in
-                    if errorCode == 0 {
+                NextcloudKit.shared.downloadContent(serverUrl: activity.icon) { _, data, error in
+                    if error == .success {
                         do {
                             try data!.write(to: NSURL(fileURLWithPath: fileNameLocalPath) as URL, options: .atomic)
                             self.tableView.reloadData()
@@ -356,7 +360,7 @@ extension NCActivity {
         if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
             bottom = -mainTabBar.getHight()
         }
-        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .gray)
+        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .medium)
 
         let dispatchGroup = DispatchGroup()
         loadComments(disptachGroup: dispatchGroup)
@@ -403,11 +407,11 @@ extension NCActivity {
         guard showComments, let metadata = metadata else { return }
         disptachGroup?.enter()
 
-        NCCommunication.shared.getComments(fileId: metadata.fileId) { account, comments, errorCode, errorDescription in
-            if errorCode == 0, let comments = comments {
+        NextcloudKit.shared.getComments(fileId: metadata.fileId) { account, comments, data, error in
+            if error == .success, let comments = comments {
                 NCManageDatabase.shared.addComments(comments, account: metadata.account, objectId: metadata.fileId)
-            } else if errorCode != NCGlobal.shared.errorResourceNotFound {
-                NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+            } else if error.errorCode != NCGlobal.shared.errorResourceNotFound {
+                NCContentPresenter.shared.showError(error: error)
             }
 
             if let disptachGroup = disptachGroup {
@@ -427,20 +431,20 @@ extension NCActivity {
 
         disptachGroup.enter()
 
-        NCCommunication.shared.getActivity(
+        NextcloudKit.shared.getActivity(
             since: 0,
             limit: 1,
             objectId: nil,
             objectType: objectType,
-            previews: true) { account, _, activityFirstKnown, activityLastGiven, errorCode, _ in
+            previews: true) { account, _, activityFirstKnown, activityLastGiven, data, error in
                 defer { disptachGroup.leave() }
 
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
-                guard errorCode == 0,
+                guard error == .success,
                       account == self.appDelegate.account,
                       largestActivityId > resultActivityId
                 else {
-                    self.hasActivityToLoad = errorCode == 304 ? false : self.hasActivityToLoad
+                    self.hasActivityToLoad = error.errorCode == NCGlobal.shared.errorNotModified ? false : self.hasActivityToLoad
                     return
                 }
 
@@ -454,18 +458,18 @@ extension NCActivity {
         var resultActivityId = 0
         disptachGroup.enter()
 
-        NCCommunication.shared.getActivity(
+        NextcloudKit.shared.getActivity(
             since: idActivity,
             limit: min(limit, 200),
             objectId: metadata?.fileId,
             objectType: objectType,
-            previews: true) { account, activities, activityFirstKnown, activityLastGiven, errorCode, _ in
+            previews: true) { account, activities, activityFirstKnown, activityLastGiven, data, error in
                 defer { disptachGroup.leave() }
-                guard errorCode == 0,
+                guard error == .success,
                       account == self.appDelegate.account,
                       !activities.isEmpty
                 else {
-                    self.hasActivityToLoad = errorCode == 304 ? false : self.hasActivityToLoad
+                    self.hasActivityToLoad = error.errorCode == NCGlobal.shared.errorNotModified ? false : self.hasActivityToLoad
                     return
                 }
                 NCManageDatabase.shared.addActivity(activities, account: account)
@@ -514,11 +518,11 @@ extension NCActivity: NCShareCommentsCellDelegate {
                     alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
                         guard let message = alert.textFields?.first?.text, message != "" else { return }
 
-                        NCCommunication.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message) { _, errorCode, errorDescription in
-                            if errorCode == 0 {
+                        NextcloudKit.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message) { _, error in
+                            if error == .success {
                                 self.loadComments()
                             } else {
-                                NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                                NCContentPresenter.shared.showError(error: error)
                             }
                         }
                     }))
@@ -535,11 +539,11 @@ extension NCActivity: NCShareCommentsCellDelegate {
                 action: { _ in
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
 
-                    NCCommunication.shared.deleteComments(fileId: metadata.fileId, messageId: tableComments.messageId) { _, errorCode, errorDescription in
-                        if errorCode == 0 {
+                    NextcloudKit.shared.deleteComments(fileId: metadata.fileId, messageId: tableComments.messageId) { _, error in
+                        if error == .success {
                             self.loadComments()
                         } else {
-                            NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                            NCContentPresenter.shared.showError(error: error)
                         }
                     }
                 }

+ 1 - 1
iOSClient/Activity/NCActivityCommentView.swift

@@ -48,7 +48,7 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
         } else {
             labelUser.text = account.displayName
         }
-        labelUser.textColor = NCBrandColor.shared.label
+        labelUser.textColor = .label
     }
 
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {

+ 33 - 46
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -22,8 +22,9 @@
 //
 
 import Foundation
-import NCCommunication
+import NextcloudKit
 import FloatingPanel
+import JGProgressHUD
 
 class NCActivityCollectionViewCell: UICollectionViewCell {
 
@@ -108,7 +109,8 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
                         viewController.trashPath = result.filePath
                         (responder as? UIViewController)!.navigationController?.pushViewController(viewController, animated: true)
                     } else {
-                        NCContentPresenter.shared.messageNotification("_error_", description: "_trash_file_not_found_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError)
+                        let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_trash_file_not_found_")
+                        NCContentPresenter.shared.showError(error: error)
                     }
                 }
             }
@@ -139,54 +141,39 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
                 }
             }
 
-            var pathComponents = activityPreview.link.components(separatedBy: "?")
-            pathComponents = pathComponents[1].components(separatedBy: "&")
-            var serverUrlFileName = pathComponents[0].replacingOccurrences(of: "dir=", with: "").removingPercentEncoding!
-            serverUrlFileName = NCUtilityFileSystem.shared.getHomeServer(account: activityPreview.account) + serverUrlFileName + "/" + activitySubjectRich.name
-            let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(activitySubjectRich.id, fileNameView: activitySubjectRich.name)!
-
-            if let backgroundView = appDelegate.window?.rootViewController?.view {
-                NCActivityIndicator.shared.start(backgroundView: backgroundView)
+            let hud = JGProgressHUD()
+            hud.indicatorView = JGProgressHUDRingIndicatorView()
+            if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
+                indicatorView.ringWidth = 1.5
             }
-
-            NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
-
-            }, taskHandler: { _ in
-
-            }, progressHandler: { _ in
-
-            }) { account, _, _, _, _, _, errorCode, _ in
-
-                if account == self.appDelegate.account && errorCode == 0 {
-
-                    let serverUrl = (serverUrlFileName as NSString).deletingLastPathComponent
-                    let fileName = (serverUrlFileName as NSString).lastPathComponent
-                    let serverUrlFileName = serverUrl + "/" + fileName
-
-                    NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName) { account, metadata, errorCode, _ in
-
-                        NCActivityIndicator.shared.stop()
-
-                        DispatchQueue.main.async {
-                            if account == self.appDelegate.account, errorCode == 0, let metadata = metadata {
-
-                                // move from id to oc:id + instanceid (ocId)
-                                let atPath = CCUtility.getDirectoryProviderStorage()! + "/" + activitySubjectRich.id
-                                let toPath = CCUtility.getDirectoryProviderStorage()! + "/" + metadata.ocId
-
-                                CCUtility.moveFile(atPath: atPath, toPath: toPath)
-
-                                NCManageDatabase.shared.addMetadata(metadata)
-                                if let viewController = self.viewController {
-                                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: cell?.imageView.image)
-                                }
+            guard let view = appDelegate.window?.rootViewController?.view else { return }
+            hud.show(in: view)
+
+            NextcloudKit.shared.getFileFromFileId(fileId: String(activityPreview.fileId)) { account, file, data, error in
+                if let file = file {
+
+                    let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: file.e2eEncrypted, account: account)
+                    NCManageDatabase.shared.addMetadata(metadata)
+
+                    let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
+                    let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+
+                    NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
+                    }, taskHandler: { _ in
+                    }, progressHandler: { progress in
+                        hud.progress = Float(progress.fractionCompleted)
+                    }) { account, _, _, _, _, _, error in
+                        hud.dismiss()
+                        if account == self.appDelegate.account && error == .success {
+                            NCManageDatabase.shared.addLocalFile(metadata: metadata)
+                            if let viewController = self.viewController {
+                                NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: cell?.imageView.image)
                             }
                         }
                     }
-
-                } else {
-
-                    NCActivityIndicator.shared.stop()
+                } else if error != .success {
+                    hud.dismiss()
+                    NCContentPresenter.shared.showError(error: error)
                 }
             }
         }

+ 193 - 158
iOSClient/AppDelegate.swift

@@ -23,10 +23,11 @@
 
 import UIKit
 import BackgroundTasks
-import NCCommunication
+import NextcloudKit
 import TOPasscodeViewController
 import LocalAuthentication
 import Firebase
+import WidgetKit
 
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, TOPasscodeViewControllerDelegate, NCAccountRequestDelegate, NCViewCertificateDetailsDelegate, NCUserBaseUrl {
@@ -41,7 +42,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     @objc var password: String = ""
 
     var deletePasswordSession: Bool = false
-    var activeAppConfigView: NCAppConfigView?
     var activeLogin: NCLogin?
     var activeLoginWeb: NCLoginWeb?
     var activeServerUrl: String = ""
@@ -60,16 +60,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var shares: [tableShare] = []
     var timerErrorNetworking: Timer?
 
-    var errorITMS90076: Bool = false
-
     private var privacyProtectionWindow: UIWindow?
-    
+
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
 
         let userAgent = CCUtility.getUserAgent() as String
-        let isSimulatorOrTestFlight = NCUtility.shared.isSimulatorOrTestFlight()
         let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())
 
+        // Register initialize
+        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
+
         UserDefaults.standard.register(defaults: ["UserAgent": userAgent])
         if !CCUtility.getDisableCrashservice() && !NCBrandOptions.shared.disable_crash_service {
             FirebaseApp.configure()
@@ -78,47 +78,38 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         CCUtility.createDirectoryStandard()
         CCUtility.emptyTemporaryDirectory()
 
-        NCCommunicationCommon.shared.setup(delegate: NCNetworking.shared)
-        NCCommunicationCommon.shared.setup(userAgent: userAgent)
+        NKCommon.shared.setup(delegate: NCNetworking.shared)
+        NKCommon.shared.setup(userAgent: userAgent)
 
         startTimerErrorNetworking()
 
         // LOG
         var levelLog = 0
         if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
-            NCCommunicationCommon.shared.pathLog = pathDirectoryGroup
+            NKCommon.shared.pathLog = pathDirectoryGroup
         }
 
         if NCBrandOptions.shared.disable_log {
 
-            NCUtilityFileSystem.shared.deleteFile(filePath: NCCommunicationCommon.shared.filenamePathLog)
-            NCUtilityFileSystem.shared.deleteFile(filePath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NCCommunicationCommon.shared.filenameLog)
+            NCUtilityFileSystem.shared.deleteFile(filePath: NKCommon.shared.filenamePathLog)
+            NCUtilityFileSystem.shared.deleteFile(filePath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NKCommon.shared.filenameLog)
 
         } else {
 
             levelLog = CCUtility.getLogLevel()
-            NCCommunicationCommon.shared.levelLog = levelLog
-            NCCommunicationCommon.shared.copyLogToDocumentDirectory = true
-            if isSimulatorOrTestFlight {
-                NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
-            } else {
-                NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS)
-            }
+            NKCommon.shared.levelLog = levelLog
+            NKCommon.shared.copyLogToDocumentDirectory = true
+            NKCommon.shared.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS + " in state \(UIApplication.shared.applicationState.rawValue) where (0 active, 1 inactive, 2 background).")
         }
 
         // LOG Account
         if let account = NCManageDatabase.shared.getActiveAccount() {
-            NCCommunicationCommon.shared.writeLog("Account active \(account.account)")
+            NKCommon.shared.writeLog("Account active \(account.account)")
             if CCUtility.getPassword(account.account).isEmpty {
-                NCCommunicationCommon.shared.writeLog("PASSWORD NOT FOUND for \(account.account)")
+                NKCommon.shared.writeLog("[ERROR] PASSWORD NOT FOUND for \(account.account)")
             }
         }
 
-        // ITMS-90076: Potential Loss of Keychain Access
-        if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty, NCUtility.shared.getVersionApp(withBuild: false).starts(with: "4.4") {
-            errorITMS90076 = true
-        }
-
         // Activate user account
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
 
@@ -147,13 +138,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         // Create user color
         NCBrandColor.shared.createUserColors()
 
-        // initialize
-        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, userInfo:["atStart":1])
-
-        // Process upload
-        networkingProcessUpload = NCNetworkingProcessUpload()
-
         // Push Notification & display notification
         application.registerForRemoteNotifications()
         UNUserNotificationCenter.current().delegate = self
@@ -167,15 +151,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
 
         // Background task: register
-        if #available(iOS 13.0, *) {
-            BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
-                self.handleRefreshTask(task)
-            }
-            BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in
-                self.handleProcessingTask(task)
-            }
-        } else {
-            application.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
+        BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
+            self.handleRefreshTask(task)
+        }
+        BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in
+            self.handleProcessingTask(task)
         }
 
         // Intro
@@ -206,47 +186,43 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Life Cycle
 
-    // L' applicazione entrerà in primo piano (attivo sempre)
+    // L' applicazione entrerà in attivo (sempre)
     func applicationDidBecomeActive(_ application: UIApplication) {
 
+        NKCommon.shared.writeLog("[INFO] Application did become active")
+
         self.deletePasswordSession = false
 
         if !NCAskAuthorization.shared.isRequesting {
-            // Privacy
             hidePrivacyProtectionWindow()
         }
 
         NCSettingsBundleHelper.setVersionAndBuildNumber()
 
-        if account == "" { return }
-
-        networkingProcessUpload?.verifyUploadZombie()
+        if !account.isEmpty {
+            networkingProcessUpload?.verifyUploadZombie()
+        }
 
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidBecomeActive)
     }
 
-    // L' applicazione entrerà in primo piano (attivo solo dopo il background)
+    // L' applicazione entrerà in primo piano (dopo il background)
     func applicationWillEnterForeground(_ application: UIApplication) {
+        guard !account.isEmpty, let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return }
 
-        if account == "" { return }
-        guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return }
+        NKCommon.shared.writeLog("[INFO] Application will enter in foreground")
 
         // Account changed ??
         if activeAccount.account != account {
             settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))
-
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize)
-        }
-
-        NCCommunicationCommon.shared.writeLog("Application will enter in foreground")
-
-        // START TIMER UPLOAD PROCESS
-        if NCUtility.shared.isSimulator() {
-            networkingProcessUpload?.startTimer()
+        } else {
+            // Initialize Auto upload
+            NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+                NKCommon.shared.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
+                // START UPLOAD PROCESS
+                DispatchQueue.main.async { self.networkingProcessUpload = NCNetworkingProcessUpload() }
+            }
         }
-        
-        // Initialize Auto upload
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in }
 
         // Required unsubscribing / subscribing
         NCPushNotification.shared().pushNotification()
@@ -264,14 +240,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // L' applicazione si dimetterà dallo stato di attivo
     func applicationWillResignActive(_ application: UIApplication) {
+        guard !account.isEmpty else { return }
 
-        if account == "" { return }
+        NKCommon.shared.writeLog("[INFO] Application will resign active")
 
         if CCUtility.getPrivacyScreenEnabled() {
             // Privacy
             showPrivacyProtectionWindow()
         }
 
+        // Reload Widget
+        WidgetCenter.shared.reloadAllTimelines()
+
         // Clear operation queue
         NCOperationQueue.shared.cancelAllQueue()
         // Clear download
@@ -288,18 +268,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // L' applicazione è entrata nello sfondo
     func applicationDidEnterBackground(_ application: UIApplication) {
+        guard !account.isEmpty else { return }
 
-        if account == "" { return }
+        NKCommon.shared.writeLog("[INFO] Application did enter in background")
 
-        // STOP TIMER UPLOAD PROCESS
-        if NCUtility.shared.isSimulator() {
-            networkingProcessUpload?.stopTimer()
-        }
+        // STOP UPLOAD PROCESS
+        networkingProcessUpload?.stopTimer()
 
-        if #available(iOS 13.0, *) {
-            scheduleAppRefresh()
-            scheduleBackgroundProcessing()
-        }
+        scheduleAppRefresh()
+        scheduleAppProcessing()
 
         // Passcode
         presentPasscode { }
@@ -311,7 +288,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func applicationWillTerminate(_ application: UIApplication) {
 
         NCNetworking.shared.cancelAllDownloadTransfer()
-        NCCommunicationCommon.shared.writeLog("bye bye")
+
+        let content = UNMutableNotificationContent()
+        content.title = NCBrandOptions.shared.brand
+        content.body = NSLocalizedString("_keep_running_", comment: "")
+        let req = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
+        let notificationCenter = UNUserNotificationCenter.current()
+        notificationCenter.add(req)
+
+        NKCommon.shared.writeLog("bye bye")
     }
 
     // MARK: -
@@ -319,13 +304,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     @objc private func initialize() {
         guard !account.isEmpty else { return }
 
-        NCCommunicationCommon.shared.writeLog("initialize Main")
+        NKCommon.shared.writeLog("[INFO] initialize Main")
 
         // Registeration push notification
         NCPushNotification.shared().pushNotification()
 
         // Start Auto Upload
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in }
+        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+            NKCommon.shared.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
+            DispatchQueue.main.async { self.networkingProcessUpload = NCNetworkingProcessUpload() }
+        }
 
         // Start services
         NCService.shared.startRequestServicesServer()
@@ -333,6 +321,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         // close detail
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuDetailClose)
 
+        // Reload Widget
+        WidgetCenter.shared.reloadAllTimelines()
+
         // Registeration domain File Provider
         // FileProviderDomain *fileProviderDomain = [FileProviderDomain new];
         // [fileProviderDomain removeAllDomains];
@@ -341,100 +332,80 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Background Task
 
-    @available(iOS 13.0, *)
+    /*
+    @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage.
+     < MAX 30 seconds >
+     */
     func scheduleAppRefresh() {
 
         let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask)
-        request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes.
+        request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
         do {
             try BGTaskScheduler.shared.submit(request)
-            NCCommunicationCommon.shared.writeLog("Refresh task success submit request \(request)")
+            NKCommon.shared.writeLog("[SUCCESS] Refresh task success submit request 60 seconds \(request)")
         } catch {
-            NCCommunicationCommon.shared.writeLog("Refresh task failed to submit request: \(error)")
+            NKCommon.shared.writeLog("[ERROR] Refresh task failed to submit request: \(error)")
         }
     }
 
-    @available(iOS 13.0, *)
-    func scheduleBackgroundProcessing() {
+    /*
+     @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week.
+     < MAX over 1 minute >
+     */
+    func scheduleAppProcessing() {
 
         let request = BGProcessingTaskRequest(identifier: NCGlobal.shared.processingTask)
         request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes.
-        request.requiresNetworkConnectivity = true
+        request.requiresNetworkConnectivity = false
         request.requiresExternalPower = false
         do {
             try BGTaskScheduler.shared.submit(request)
-            NCCommunicationCommon.shared.writeLog("Background Processing task success submit request \(request)")
+            NKCommon.shared.writeLog("[SUCCESS] Background Processing task success submit request 5 minutes \(request)")
         } catch {
-            NCCommunicationCommon.shared.writeLog("Background Processing task failed to submit request: \(error)")
+            NKCommon.shared.writeLog("[ERROR] Background Processing task failed to submit request: \(error)")
         }
     }
 
-    @available(iOS 13.0, *)
     func handleRefreshTask(_ task: BGTask) {
-
-        if account == "" {
+        scheduleAppRefresh()
+        
+        guard !account.isEmpty else {
             task.setTaskCompleted(success: true)
             return
         }
 
-        NCCommunicationCommon.shared.writeLog("Start handler refresh task [Auto upload]")
-
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in
-            DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
-                NCCommunicationCommon.shared.writeLog("Completition handler refresh task with [Auto upload]")
-                task.setTaskCompleted(success: true)
-            }
+        NKCommon.shared.setup(delegate: NCNetworking.shared)
+        NKCommon.shared.writeLog("[INFO] Start handler refresh task [Auto upload]")
+        
+        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+            NKCommon.shared.writeLog("[INFO] Completition handler refresh task [Auto upload] with \(items) uploads")
+            task.setTaskCompleted(success: true)
         }
     }
 
-    @available(iOS 13.0, *)
     func handleProcessingTask(_ task: BGTask) {
-
-        if account == "" {
+        scheduleAppProcessing()
+        
+        guard !account.isEmpty else {
             task.setTaskCompleted(success: true)
             return
         }
 
-        NCCommunicationCommon.shared.writeLog("Start handler processing task [Synchronize Favorite & Offline]")
-
-        NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorReadFile) { _, _, errorCode, _ in
-            NCCommunicationCommon.shared.writeLog("Completition listing favorite with error: \(errorCode)")
-        }
+        NKCommon.shared.setup(delegate: NCNetworking.shared)
+        NKCommon.shared.writeLog("[INFO] Start handler processing task [Reload widget]")
 
-        NCService.shared.synchronizeOffline(account: account)
+        WidgetCenter.shared.reloadAllTimelines()
 
-        DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
-            NCCommunicationCommon.shared.writeLog("Completition handler processing task [Synchronize Favorite & Offline]")
-            task.setTaskCompleted(success: true)
-        }
-    }
-
-    // MARK: - Fetch
-
-    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
-
-        if account == "" {
-            completionHandler(UIBackgroundFetchResult.noData)
-            return
-        }
-
-        NCCommunicationCommon.shared.writeLog("Start perform Fetch [Auto upload]")
-
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
-            NCCommunicationCommon.shared.writeLog("Completition perform Fetch with \(items) uploads [Auto upload]")
-            if items == 0 {
-                completionHandler(UIBackgroundFetchResult.noData)
-            } else {
-                completionHandler(UIBackgroundFetchResult.newData)
-            }
-        }
+        task.setTaskCompleted(success: true)
     }
 
     // MARK: - Background Networking Session
 
     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
 
-        NCCommunicationCommon.shared.writeLog("Start handle Events For Background URLSession: \(identifier)")
+        NKCommon.shared.writeLog("[INFO] Start handle Events For Background URLSession: \(identifier)")
+        // Reload Widget
+        WidgetCenter.shared.reloadAllTimelines()
         backgroundSessionCompletionHandler = completionHandler
     }
 
@@ -449,8 +420,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
-        NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: self.window?.rootViewController) { errorCode in
-            if errorCode == 0 {
+        NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: self.window?.rootViewController) { error in
+            if error == .success {
                 NCPushNotification.shared().registerForRemoteNotifications(withDeviceToken: deviceToken)
             }
         }
@@ -466,18 +437,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) {
 
-        // use appConfig [MDM]
-        if NCBrandOptions.shared.use_configuration {
-
-            if activeAppConfigView?.view.window == nil {
-                activeAppConfigView = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCAppConfigView") as? NCAppConfigView
-                showLoginViewController(activeAppConfigView, contextViewController: viewController)
-            }
-            return
-        }
-
-        // only for personalized LoginWeb [customer]
-        if NCBrandOptions.shared.use_login_web_personalized {
+        // [WEBPersonalized] [AppConfig]
+        if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {
 
             if activeLoginWeb?.view.window == nil {
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
@@ -576,19 +537,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         let certificateHostSavedPath = CCUtility.getDirectoryCerificates()! + "/" + host + ".der"
         var title = NSLocalizedString("_ssl_certificate_changed_", comment: "")
-        
+
         if !FileManager.default.fileExists(atPath: certificateHostSavedPath) {
             title = NSLocalizedString("_connect_server_anyway_", comment: "")
         }
-        
+
         let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert)
-        
+
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { action in
             NCNetworking.shared.writeCertificate(host: host)
         }))
-        
+
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in }))
-        
+
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { action in
             if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
                 let viewController = navigationController.topViewController as! NCViewCertificateDetails
@@ -597,7 +558,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 self.window?.rootViewController?.present(navigationController, animated: true)
             }
         }))
-        
+
         window?.rootViewController?.present(alertController, animated: true)
     }
 
@@ -609,6 +570,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     @objc func settingAccount(_ account: String, urlBase: String, user: String, userId: String, password: String) {
 
+        let accountBackup = self.account
+        let userIdBackup = self.userId
+
         self.account = account
         self.urlBase = urlBase
         self.user = user
@@ -617,12 +581,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         _ = NCFunctionCenter.shared
 
-        NCCommunicationCommon.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
+        NKCommon.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
         let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
         if serverVersionMajor > 0 {
-            NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor)
+            NKCommon.shared.setup(nextcloudVersion: serverVersionMajor)
         }
         NCKTVHTTPCache.shared.restartProxy(user: user, password: password)
+
+        DispatchQueue.main.async {
+            if UIApplication.shared.applicationState != .background && (accountBackup != account || userIdBackup != userId) {
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, second: 0.2)
+            }
+        }
     }
 
     @objc func deleteAccount(_ account: String, wipe: Bool) {
@@ -663,8 +633,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             NCNetworking.shared.cancelAllTask()
 
             settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account))
-
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize)
         }
     }
 
@@ -699,7 +667,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height+20)
                 popup.backgroundAlpha = 0.8
 
-                UIApplication.shared.keyWindow?.rootViewController?.present(popup, animated: true)
+                window?.rootViewController?.present(popup, animated: true)
                 
                 vcAccountRequest.startTimer()
             }
@@ -814,8 +782,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // MARK: - Open URL
 
     func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
-
-        if account == "" { return false }
+        guard !account.isEmpty else { return false }
 
         let scheme = url.scheme
         let action = url.host
@@ -823,7 +790,73 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         var serverUrl: String = ""
         var matchedAccount: tableAccount?
 
-        if scheme == "nextcloud" && action == "open-file" {
+        /*
+         Example:
+         nextcloud://open-action?action=create-voice-memo
+         */
+
+        if scheme == "nextcloud" && action == "open-action" {
+
+            if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
+                let queryItems = urlComponents.queryItems
+                guard let actionScheme = CCUtility.value(forKey: "action", fromQueryItems: queryItems), let rootViewController = window?.rootViewController else { return false }
+                
+                switch actionScheme {
+                case NCGlobal.shared.actionUploadAsset:
+
+                    NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: rootViewController) { hasPermission in
+                        if hasPermission {
+                            NCPhotosPickerViewController.init(viewController: rootViewController, maxSelectedAssets: 0, singleSelectedMode: false)
+                        }
+                    }
+                    
+                case NCGlobal.shared.actionScanDocument:
+                    
+                    NCCreateScanDocument.shared.openScannerDocument(viewController: rootViewController)
+                    
+                case NCGlobal.shared.actionTextDocument:
+                    
+                    guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController(), let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account), let directEditingCreator = directEditingCreators.first(where: { $0.editor == NCGlobal.shared.editorText}) else { return false }
+                    
+                    navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
+
+                    let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
+                    viewController.editorId = NCGlobal.shared.editorText
+                    viewController.creatorId = directEditingCreator.identifier
+                    viewController.typeTemplate = NCGlobal.shared.templateDocument
+                    viewController.serverUrl = activeServerUrl
+                    viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
+
+                    rootViewController.present(navigationController, animated: true, completion: nil)
+                    
+                case NCGlobal.shared.actionVoiceMemo:
+                    
+                    NCAskAuthorization.shared.askAuthorizationAudioRecord(viewController: rootViewController) { hasPermission in
+                        if hasPermission {
+                            let fileName = CCUtility.createFileNameDate(NSLocalizedString("_voice_memo_filename_", comment: ""), extension: "m4a")!
+                            let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as! NCAudioRecorderViewController
+
+                            viewController.delegate = self
+                            viewController.createRecorder(fileName: fileName)
+                            viewController.modalTransitionStyle = .crossDissolve
+                            viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
+
+                            rootViewController.present(viewController, animated: true, completion: nil)
+                        }
+                    }
+
+                default:
+                    print("No action")
+                }
+            }
+        }
+
+        /*
+         Example:
+         nextcloud://open-file?path=Talk/IMG_0000123.jpg&user=marinofaggiana&link=https://cloud.nextcloud.com/f/123
+         */
+
+        else if scheme == "nextcloud" && action == "open-file" {
 
             if !isSearchingMode, let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
 
@@ -862,7 +895,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                         }
 
                         DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                            NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: fileName)
+                            NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: nil, fileNameOpen: fileName)
                         }
 
                     } else {
@@ -880,6 +913,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                     }
                 }
             }
+        } else {
+            app.open(url)
         }
 
         return true
@@ -908,6 +943,6 @@ extension AppDelegate: NCAudioRecorderViewControllerDelegate {
 extension AppDelegate: NCCreateFormUploadConflictDelegate {
     func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
         guard let metadatas = metadatas, !metadatas.isEmpty else { return }
-        networkingProcessUpload?.createProcessUploads(metadatas: metadatas)
+        networkingProcessUpload?.createProcessUploads(metadatas: metadatas, completion: { _ in })
     }
 }

+ 9 - 21
iOSClient/Brand/Intro/NCIntroViewController.swift

@@ -60,20 +60,12 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
             textColorOpponent = .black
         }
 
-        if #available(iOS 13.0, *) {
-            let navBarAppearance = UINavigationBarAppearance()
-            navBarAppearance.configureWithTransparentBackground()
-            navBarAppearance.shadowColor = .clear
-            navBarAppearance.shadowImage = UIImage()
-            self.navigationController?.navigationBar.standardAppearance = navBarAppearance
-            self.navigationController?.view.backgroundColor = NCBrandColor.shared.customer
-        } else {
-            self.navigationController?.navigationBar.isTranslucent = true
-            self.navigationController?.navigationBar.shadowImage = UIImage()
-            self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
-            self.navigationController?.navigationBar.backgroundColor = .clear
-            self.navigationController?.navigationBar.barTintColor = NCBrandColor.shared.customer
-        }
+        let navBarAppearance = UINavigationBarAppearance()
+        navBarAppearance.configureWithTransparentBackground()
+        navBarAppearance.shadowColor = .clear
+        navBarAppearance.shadowImage = UIImage()
+        self.navigationController?.navigationBar.standardAppearance = navBarAppearance
+        self.navigationController?.view.backgroundColor = NCBrandColor.shared.customer
         self.navigationController?.navigationBar.tintColor = textColor
 
         pageControl.currentPageIndicatorTintColor = textColor
@@ -108,14 +100,10 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     }
 
     override var preferredStatusBarStyle: UIStatusBarStyle {
-        if #available(iOS 13.0, *) {
-            if traitCollection.userInterfaceStyle == .light {
-                return .lightContent
-            } else {
-                return .darkContent
-            }
-        } else {
+        if traitCollection.userInterfaceStyle == .light {
             return .lightContent
+        } else {
+            return .darkContent
         }
     }
 

+ 42 - 238
iOSClient/Brand/NCBrand.swift

@@ -23,20 +23,6 @@
 
 import UIKit
 
-// MARK: - Configuration
-
-@objc class NCBrandConfiguration: NSObject {
-    @objc static let shared: NCBrandConfiguration = {
-        let instance = NCBrandConfiguration()
-        return instance
-    }()
-
-    @objc public let configuration_bundleId: String = "it.twsweb.Nextcloud"
-    @objc public let configuration_serverUrl: String = "serverUrl"
-    @objc public let configuration_username: String = "username"
-    @objc public let configuration_password: String = "password"
-}
-
 // MARK: - Options
 
 @objc class NCBrandOptions: NSObject {
@@ -46,20 +32,19 @@ import UIKit
     }()
 
     @objc public var brand: String = "Nextcloud"
-    // @objc public var mailMe:                            String = "ios@nextcloud.com"                              // Deprecated
     @objc public var textCopyrightNextcloudiOS: String = "Nextcloud Liquid for iOS %@ © 2022"
     @objc public var textCopyrightNextcloudServer: String = "Nextcloud Server %@"
     @objc public var loginBaseUrl: String = "https://cloud.nextcloud.com"
     @objc public var pushNotificationServerProxy: String = "https://push-notifications.nextcloud.com"
     @objc public var linkLoginHost: String = "https://nextcloud.com/install"
     @objc public var linkloginPreferredProviders: String = "https://nextcloud.com/signup-ios"
-    @objc public var webLoginAutenticationProtocol: String = "nc://"                                            // example "abc://"
+    @objc public var webLoginAutenticationProtocol: String = "nc://"                                                // example "abc://"
     @objc public var privacy: String = "https://nextcloud.com/privacy"
     @objc public var sourceCode: String = "https://github.com/nextcloud/ios"
 
     // Personalized
-    @objc public var webCloseViewProtocolPersonalized: String = ""                                                 // example "abc://change/plan"      Don't touch me !!
-    @objc public var folderBrandAutoUpload: String = ""                                                 // example "_auto_upload_folder_"   Don't touch me !!
+    @objc public var webCloseViewProtocolPersonalized: String = ""                                                  // example "abc://change/plan"      Don't touch me !!
+    @objc public var folderBrandAutoUpload: String = ""                                                             // example "_auto_upload_folder_"   Don't touch me !!
 
     // Auto Upload default folder
     @objc public var folderDefaultAutoUpload: String = "Photos"
@@ -68,16 +53,17 @@ import UIKit
     @objc public var capabilitiesGroups: String = "group.it.twsweb.Crypto-Cloud"
 
     // User Agent
-    @objc public var userAgent: String = "Nextcloud-iOS"                                    // Don't touch me !!
+    @objc public var userAgent: String = "Nextcloud-iOS"                                                            // Don't touch me !!
 
-    // Options
+    // BRAND ONLY
     @objc public var use_login_web_personalized:        Bool = false                                                // Don't touch me !!
+    @objc public var use_AppConfig:                     Bool = false                                                // Don't touch me !!
+    
+    // Options
     @objc public var use_default_auto_upload:           Bool = false
     @objc public var use_themingColor:                  Bool = true
-    //@objc public var use_themingBackground:             Bool = true                                               // Deprecated
     @objc public var use_themingLogo:                   Bool = false
     @objc public var use_storeLocalAutoUploadAll:       Bool = false
-    @objc public var use_configuration:                 Bool = false                                                // Don't touch me !!
     @objc public var use_loginflowv2:                   Bool = false                                                // Don't touch me !!
 
     @objc public var disable_intro:                     Bool = false
@@ -87,7 +73,6 @@ import UIKit
     @objc public var disable_more_external_site:        Bool = false
     @objc public var disable_openin_file:               Bool = false                                                // Don't touch me !!
     @objc public var disable_crash_service:             Bool = false
-    @objc public var disable_request_account:           Bool = false
     @objc public var disable_log:                       Bool = false
 
     override init() {
@@ -95,6 +80,35 @@ import UIKit
         if folderBrandAutoUpload != "" {
             folderDefaultAutoUpload = folderBrandAutoUpload
         }
+        
+        // wrapper AppConfig
+        if let configurationManaged = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), use_AppConfig {
+            
+            if let str = configurationManaged[NCGlobal.shared.configuration_brand] as? String {
+                brand = str
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_intro] as? String {
+                disable_intro = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_multiaccount] as? String {
+                disable_multiaccount = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_crash_service] as? String {
+                disable_crash_service = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_log] as? String {
+                disable_log = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_manage_account] as? String {
+                disable_manage_account = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_more_external_site] as? String {
+                disable_more_external_site = (str as NSString).boolValue
+            }
+            if let str = configurationManaged[NCGlobal.shared.configuration_disable_openin_file] as? String {
+                disable_openin_file = (str as NSString).boolValue
+            }
+        }
     }
 }
 
@@ -146,11 +160,11 @@ class NCBrandColor: NSObject {
     }
 
     // Color
-    @objc public let customer: UIColor = UIColor(red: 0.0/255.0, green: 130.0/255.0, blue: 201.0/255.0, alpha: 1.0)    // BLU NC : #0082c9
+    @objc public let customer: UIColor = UIColor(red: 0.0/255.0, green: 130.0/255.0, blue: 201.0/255.0, alpha: 1.0)     // BLU NC : #0082c9
     @objc public var customerText: UIColor = .white
 
-    @objc public var brand: UIColor                                                                                 // don't touch me
-    @objc public var brandElement: UIColor                                                                                 // don't touch me
+    @objc public var brand: UIColor                                                                                     // don't touch me
+    @objc public var brandElement: UIColor                                                                              // don't touch me
     @objc public var brandText: UIColor                                                                                 // don't touch me
 
     @objc public let nextcloud: UIColor = UIColor(red: 0.0/255.0, green: 130.0/255.0, blue: 201.0/255.0, alpha: 1.0)
@@ -163,225 +177,15 @@ class NCBrandColor: NSObject {
     public var themingColorElement: String = ""
     public var themingColorText: String = ""
 
-    @objc public var annotationColor: UIColor {
-        get {
-            return .systemBlue
-        }
-    }
-
-    @objc public var systemBlue: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemBlue
-            } else {
-                return UIColor(red: 0.0, green: 122.0 / 255.0, blue: 1.0, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemIndigo: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemIndigo
-            } else {
-                return UIColor(red: 88.0 / 255.0, green: 86.0 / 255.0, blue: 214.0 / 255.0, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemPink: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemPink
-            } else {
-                return UIColor(red: 1.0, green: 45.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemTeal: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemTeal
-            } else {
-                return UIColor(red: 90.0 / 255.0, green: 200.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0)
-            }
-        }
-    }
-
     @objc public var systemMint: UIColor {
         get {
             return UIColor(red: 0.0 / 255.0, green: 199.0 / 255.0, blue: 190.0 / 255.0, alpha: 1.0)
         }
     }
 
-    @objc public var systemBackground: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemBackground
-            } else {
-                return .white
-            }
-        }
-    }
-
-    @objc public var secondarySystemBackground: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .secondarySystemBackground
-            } else {
-                return UIColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var tertiarySystemBackground: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .tertiarySystemBackground
-            } else {
-                return UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGroupedBackground: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGroupedBackground
-            } else {
-                return UIColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var secondarySystemGroupedBackground: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .secondarySystemGroupedBackground
-            } else {
-                return .white
-            }
-        }
-    }
-
-    @objc public var label: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .label
-            } else {
-                return .black
-            }
-        }
-    }
-
-    @objc public var secondaryLabel: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .secondaryLabel
-            } else {
-                return UIColor(red: 0.24, green: 0.24, blue: 0.26, alpha: 0.6)
-            }
-        }
-    }
-
-    @objc public var separator: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .separator
-            } else {
-                return UIColor(red: 0.89, green: 0.89, blue: 0.89, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var opaqueSeparator: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .opaqueSeparator
-            } else {
-                return UIColor(red: 0.78, green: 0.78, blue: 0.78, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray
-            } else {
-                return UIColor(red: 0.56, green: 0.56, blue: 0.58, alpha: 1.0)
-            }
-        }
-    }
-
     @objc public var systemGray1: UIColor {
         get {
-            if #available(iOS 13, *) {
-                return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0)
-            } else {
-                return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray2: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray2
-            } else {
-                return UIColor(red: 0.68, green: 0.68, blue: 0.7, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray3: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray3
-            } else {
-                return UIColor(red: 0.78, green: 0.78, blue: 0.8, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray4: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray4
-            } else {
-                return UIColor(red: 0.82, green: 0.82, blue: 0.84, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray5: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray5
-            } else {
-                return UIColor(red: 0.9, green: 0.9, blue: 0.92, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemGray6: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemGray6
-            } else {
-                return UIColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1.0)
-            }
-        }
-    }
-
-    @objc public var systemFill: UIColor {
-        get {
-            if #available(iOS 13, *) {
-                return .systemFill
-            } else {
-                return UIColor(red: 120/255, green: 120/255, blue: 120/255, alpha: 1.0)
-            }
+            return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0)
         }
     }
 
@@ -407,7 +211,7 @@ class NCBrandColor: NSObject {
 
         cacheImages.favorite = NCUtility.shared.loadImage(named: "star.fill", color: yellowFavorite)
         cacheImages.comment = UIImage(named: "comment")!.image(color: gray, size: 50)
-        cacheImages.livePhoto = NCUtility.shared.loadImage(named: "livephoto", color: label)
+        cacheImages.livePhoto = NCUtility.shared.loadImage(named: "livephoto", color: .label)
         cacheImages.offlineFlag = UIImage(named: "offlineFlag")!
         cacheImages.local = UIImage(named: "local")!
 

+ 8 - 10
iOSClient/Brand/NCBridgeSwift.h

@@ -21,28 +21,26 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
-//
-// App bridge swift
-//
-// change 
-// from   : Nextcloud-Swift.h
-// to     : brand-Swift.h
-//
 #if !defined(EXTENSION)
 #import "Nextcloud-Swift.h"
 #endif
 
-// Nextcloud Share
 #if defined(EXTENSION_SHARE)
 #import "Share-Swift.h"
 #endif
 
-// Nextcloud File Provider Extension
 #if defined(EXTENSION_FILE_PROVIDER_EXTENSION)
 #import "File_Provider_Extension-Swift.h"
 #endif
 
-// Nextcloud Notification Service Extension
 #if defined(EXTENSION_NOTIFICATION_SERVICE)
 #import "Notification_Service_Extension-Swift.h"
 #endif
+
+#if defined(EXTENSION_WIDGET)
+#import "Widget-Swift.h"
+#endif
+
+#if defined(EXTENSION_WIDGETDASHBOARDINTENTHANDLER)
+#import "WidgetDashboardIntentHandler-Swift.h"
+#endif

部分文件因为文件数量过多而无法显示