Эх сурвалжийг харах

Merge pull request #2194 from nextcloud/improvements

Improvements
Marino Faggiana 2 жил өмнө
parent
commit
06827d312c

+ 4 - 0
.swiftlint.yml

@@ -41,10 +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

+ 20 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -279,6 +279,9 @@
 		F76DA963277B760E0082465B /* Queuer in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA962277B760E0082465B /* Queuer */; };
 		F76DA966277B76F30082465B /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA965277B76F30082465B /* UICKeyChainStore */; };
 		F76DA969277B77EA0082465B /* DropDown in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA968277B77EA0082465B /* DropDown */; };
+		F76DEE9728F808AF0041B1C9 /* LockscreenData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76DEE9428F808AF0041B1C9 /* LockscreenData.swift */; };
+		F76DEE9828F808AF0041B1C9 /* LockscreenWidgetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76DEE9528F808AF0041B1C9 /* LockscreenWidgetProvider.swift */; };
+		F76DEE9928F808AF0041B1C9 /* LockscreenWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76DEE9628F808AF0041B1C9 /* LockscreenWidgetView.swift */; };
 		F7707687263A853700A1BA94 /* NCContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765608E23BF813500765969 /* NCContentPresenter.swift */; };
 		F7707689263A896A00A1BA94 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
 		F770768A263A8A2500A1BA94 /* NCUtilityFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */; };
@@ -848,6 +851,9 @@
 		F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerPDFSearch.swift; sourceTree = "<group>"; };
 		F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerPDFSearchCell.xib; sourceTree = "<group>"; };
 		F76D3CF42428D0C0005DFA87 /* NCViewerPDF.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCViewerPDF.storyboard; sourceTree = "<group>"; };
+		F76DEE9428F808AF0041B1C9 /* LockscreenData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockscreenData.swift; sourceTree = "<group>"; };
+		F76DEE9528F808AF0041B1C9 /* LockscreenWidgetProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockscreenWidgetProvider.swift; sourceTree = "<group>"; };
+		F76DEE9628F808AF0041B1C9 /* LockscreenWidgetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockscreenWidgetView.swift; sourceTree = "<group>"; };
 		F76E71E42244DF6900690001 /* Zip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Zip.framework; path = Carthage/Build/iOS/Zip.framework; sourceTree = "<group>"; };
 		F76FDEAA24859C3D0095B6C2 /* Queuer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Queuer.framework; path = Carthage/Build/iOS/Queuer.framework; sourceTree = "<group>"; };
 		F771E3D020E2392D00AFB62D /* File Provider Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "File Provider Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1484,6 +1490,7 @@
 				F7346E2228B0FEBA006CE2D2 /* Assets.xcassets */,
 				F72EA95528B7BAD100C88F0C /* Dashboard */,
 				F72EA95628B7BAE700C88F0C /* Files */,
+				F76DEE9A28F808BC0041B1C9 /* Lockscreen */,
 				F77ED59628C9CEEE00E24ED0 /* Toolbar */,
 				F7346E2028B0FA3A006CE2D2 /* Widget-Brinding-header.h */,
 				F7346E1528B0EF5C006CE2D2 /* Widget.swift */,
@@ -1577,6 +1584,16 @@
 			path = NCViewerPDF;
 			sourceTree = "<group>";
 		};
+		F76DEE9A28F808BC0041B1C9 /* Lockscreen */ = {
+			isa = PBXGroup;
+			children = (
+				F76DEE9428F808AF0041B1C9 /* LockscreenData.swift */,
+				F76DEE9528F808AF0041B1C9 /* LockscreenWidgetProvider.swift */,
+				F76DEE9628F808AF0041B1C9 /* LockscreenWidgetView.swift */,
+			);
+			path = Lockscreen;
+			sourceTree = "<group>";
+		};
 		F771E3D120E2392D00AFB62D /* File Provider Extension */ = {
 			isa = PBXGroup;
 			children = (
@@ -2814,6 +2831,7 @@
 			files = (
 				F78302FD28B4C42B00B84583 /* NCUserBaseUrl.swift in Sources */,
 				F793E5A128B76541005E4B02 /* NotificationCenter+MainThread.swift in Sources */,
+				F76DEE9928F808AF0041B1C9 /* LockscreenWidgetView.swift in Sources */,
 				F78302F928B4C3E600B84583 /* NCManageDatabase+Account.swift in Sources */,
 				F7E0710128B13BB00001B882 /* DashboardData.swift in Sources */,
 				F783030328B4C4DD00B84583 /* ThreadSafeDictionary.swift in Sources */,
@@ -2823,11 +2841,13 @@
 				F78302F828B4C3E100B84583 /* NCManageDatabase+Activity.swift in Sources */,
 				F783030228B4C4B800B84583 /* NCUtility.swift in Sources */,
 				F711D63128F44801003F43C8 /* IntentHandler.swift in Sources */,
+				F76DEE9728F808AF0041B1C9 /* LockscreenData.swift in Sources */,
 				F72EA95A28B7BD0D00C88F0C /* FilesWidgetView.swift in Sources */,
 				F78302FE28B4C44700B84583 /* NCBrand.swift in Sources */,
 				F793E5A028B7651B005E4B02 /* NCViewCertificateDetails.swift in Sources */,
 				F7FABE3728D1DAD00000A325 /* Dashboard.intentdefinition in Sources */,
 				F793E59F28B764F6005E4B02 /* NCContentPresenter.swift in Sources */,
+				F76DEE9828F808AF0041B1C9 /* LockscreenWidgetProvider.swift in Sources */,
 				F78302FA28B4C3EA00B84583 /* NCManageDatabase+Metadata.swift in Sources */,
 				F783030728B4C52800B84583 /* UIColor+Extensions.swift in Sources */,
 				F783030028B4C45800B84583 /* NCGlobal.swift in Sources */,

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

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

BIN
Widget/Assets.xcassets/activity.imageset/activity.png


+ 85 - 66
Widget/Dashboard/DashboardData.swift

@@ -24,12 +24,13 @@
 import WidgetKit
 import NextcloudKit
 import RealmSwift
+import SVGKit
 
 struct DashboardDataEntry: TimelineEntry {
     let date: Date
     let datas: [DashboardData]
-    let tableDashboard: tableDashboardWidget?
-    let tableButton: [tableDashboardWidgetButton]?
+    let dashboard: tableDashboardWidget?
+    let buttons: [tableDashboardWidgetButton]?
     let isPlaceholder: Bool
     let titleImage: UIImage
     let title: String
@@ -83,20 +84,20 @@ func getDashboardDataEntry(intent: Applications?, isPreview: Bool, displaySize:
     let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems - 1])
 
     if isPreview {
-        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, tableDashboard: nil, tableButton: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard"))
+        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, tableDashboard: nil, tableButton: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", comment: "")))
+        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: recommendations
-    let id: String = intent?.identifier ?? "recommendations"
+    // 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, tableDashboard: nil, tableButton: nil, isPlaceholder: true, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "xmark.icloud", footerText: NSLocalizedString("_widget_available_nc25_", comment: "")))
+        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
@@ -130,80 +131,98 @@ func getDashboardDataEntry(intent: Applications?, isPreview: Bool, displaySize:
     let existsButton = (tableButton?.isEmpty ?? true) ? false : true
     let options = NKRequestOptions(timeout: 15, queue: NKCommon.shared.backgroundQueue)
     let title = tableDashboard?.title ?? id
-    var titleImage = UIImage(named: "widget")!
 
+    var imagetmp = UIImage(named: "widget")!
     if let fileName = tableDashboard?.iconClass {
         let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
         if let image = UIImage(contentsOfFile: fileNamePath) {
-            titleImage = image.withTintColor(.label, renderingMode: .alwaysOriginal)
+            imagetmp = image.withTintColor(.label, renderingMode: .alwaysOriginal)
         }
     }
+    let titleImage = imagetmp
         
     NextcloudKit.shared.getDashboardWidgetsApplication(id, options: options) { account, results, data, error in
-        
-        var datas = [DashboardData]()
-        
-        if let results = results {
-            for result in results {
-                if let items = result.items {
-                    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
+        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
+                                    }
                                 }
-                            }
-                            let semaphore = DispatchSemaphore(value: 0)
-                            NCUtility.shared.getWidgetImageUserData(url: url, fileName: iconFileName) { image in
-                                if let image = image {
-                                    icon = image
+
+                                do {
+                                    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) = try await NextcloudKit.shared.getPreview(url: url)
+                                            if let image = NCUtility.shared.convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName) {
+                                                icon = image
+                                            }
+                                        }
+                                    }
+                                } catch {
+                                    print(error)
                                 }
-                                semaphore.signal()
                             }
-                            semaphore.wait()
+
+                            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 }
                         }
-                        
-                        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 }
                     }
                 }
             }
-        }
-        
-        if error != .success {
-            completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, tableDashboard: tableDashboard, tableButton: tableButton, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "xmark.icloud", footerText: error.errorDescription))
-        } else if datas.isEmpty {
-            completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, tableDashboard: tableDashboard, tableButton: tableButton, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NSLocalizedString("_no_data_available_", comment: "")))
-        } else {
-            completion(DashboardDataEntry(date: Date(), datas: datas, tableDashboard: tableDashboard, tableButton: tableButton, isPlaceholder: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard"))
+
+            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"))
+            }
         }
     }
 }

+ 1 - 1
Widget/Dashboard/DashboardWidgetProvider.swift

@@ -35,7 +35,7 @@ struct DashboardWidgetProvider: IntentTimelineProvider {
         let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems])
         let title = "Dashboard"
         let titleImage = UIImage(named: "widget")!
-        return Entry(date: Date(), datas: datasPlaceholder, tableDashboard: nil, tableButton: nil, isPlaceholder: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " 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) {

+ 6 - 6
Widget/Dashboard/DashboardWidgetView.swift

@@ -69,7 +69,7 @@ struct DashboardWidgetView: View {
                                             .fill(Color(.systemGray4))
                                             .frame(width: 35, height: 35)
                                     } else if element.template {
-                                        if entry.tableDashboard?.itemIconsRound ?? false {
+                                        if entry.dashboard?.itemIconsRound ?? false {
                                             Image(uiImage: element.icon)
                                                 .renderingMode(.template)
                                                 .resizable()
@@ -90,7 +90,7 @@ struct DashboardWidgetView: View {
                                                 .cornerRadius(5)
                                         }
                                     } else {
-                                        if entry.tableDashboard?.itemIconsRound ?? false || element.avatar {
+                                        if entry.dashboard?.itemIconsRound ?? false || element.avatar {
                                             Image(uiImage: element.icon)
                                                 .resizable()
                                                 .scaledToFill()
@@ -129,14 +129,14 @@ struct DashboardWidgetView: View {
                 .padding(.top, 35)
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
 
-                if let tableButton = entry.tableButton, !tableButton.isEmpty, !entry.isPlaceholder {
-                    
+                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(tableButton, id: \.index) { element in
+                        ForEach(buttons, id: \.index) { element in
                             Link(destination: URL(string: element.link)! , label: {
                                 
                                 Text(element.text)
@@ -176,7 +176,7 @@ struct DashboardWidget_Previews: PreviewProvider {
         let datas = Array(dashboardDatasTest[0...4])
         let title = "Dashboard"
         let titleImage = UIImage(named: "widget")!
-        let entry = DashboardDataEntry(date: Date(), datas: datas, tableDashboard: nil, tableButton: nil, isPlaceholder: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: "Nextcloud 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))
     }
 }

+ 3 - 6
Widget/Files/FilesWidgetView.swift

@@ -55,14 +55,11 @@ struct FilesWidgetView: View {
                             Link(destination: element.url) {
                                 
                                 HStack {
-                                    
-                                    let subTitleColor = Color(white: 0.5)
-                                    let imageSize:CGFloat = 35
-                                    
+
                                     Image(uiImage: element.image)
                                         .resizable()
                                         .scaledToFill()
-                                        .frame(width: imageSize, height: imageSize)
+                                        .frame(width: 35, height: 35)
                                         .clipped()
                                         .cornerRadius(5)
                                     
@@ -74,7 +71,7 @@ struct FilesWidgetView: View {
                                         
                                         Text(element.subTitle)
                                             .font(.system(size: CGFloat(10)))
-                                            .foregroundColor(subTitleColor)
+                                            .foregroundColor(Color(.systemGray))
                                     }
                                     Spacer()
                                 }

+ 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)
+        }
+    }
+}

+ 73 - 0
Widget/Lockscreen/LockscreenWidgetView.swift

@@ -0,0 +1,73 @@
+//
+//  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: 3) {
+                HStack(spacing: 3) {
+                    Image("activity")
+                        .renderingMode(.template)
+                        .resizable()
+                        .scaledToFill()
+                        .frame(width: 12, height: 12)
+                    Text(NSLocalizedString("_recent_activity_", comment: ""))
+                        .font(.system(size: 13)).bold()
+                }
+                Text(entry.activity)
+                    .font(.system(size: 10))
+                    .fontWeight(.light)
+            }
+            .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")
+    }
+}

+ 18 - 0
Widget/Widget.swift

@@ -32,6 +32,7 @@ struct NextcloudWidgetBundle: WidgetBundle {
         DashboardWidget()
         FilesWidget()
         ToolbarWidget()
+        LockscreenWidget()
     }
 }
 
@@ -73,3 +74,20 @@ struct ToolbarWidget: Widget {
         .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()
+        }
+    }
+}

+ 12 - 2
WidgetDashboardIntentHandler/IntentHandler.swift

@@ -26,7 +26,17 @@ class IntentHandler: INExtension, DashboardIntentHandling {
             applications.append(application)
         }
 
-        let collection = INObjectCollection(items: applications)
-        completion(collection, nil)
+        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
     }
 }

+ 12 - 5
iOSClient/Networking/NCService.swift

@@ -273,11 +273,18 @@ class NCService: NSObject {
         let options = NKRequestOptions(queue: NKCommon.shared.backgroundQueue)
         
         NextcloudKit.shared.getDashboardWidget(options: options) { account, dashboardWidgets, data, error in
-            if error == .success, let dashboardWidgets = dashboardWidgets  {
-                NCManageDatabase.shared.addDashboardWidget(account: account, dashboardWidgets: dashboardWidgets)
-                for widget in dashboardWidgets {
-                    if let url = URL(string: widget.iconUrl), let fileName = widget.iconClass {
-                        NCUtility.shared.getWidgetImageUserData(url: url, fileName: fileName)
+            Task {
+                if error == .success, let dashboardWidgets = dashboardWidgets  {
+                    NCManageDatabase.shared.addDashboardWidget(account: account, dashboardWidgets: dashboardWidgets)
+                    for widget in dashboardWidgets {
+                        if let url = URL(string: widget.iconUrl), let fileName = widget.iconClass {
+                            do {
+                                let (_, data) = try await NextcloudKit.shared.getPreview(url: url)
+                                NCUtility.shared.convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName)
+                            } catch {
+                                print(error)
+                            }
+                        }
                     }
                 }
             }

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

@@ -891,6 +891,9 @@
 "_no_data_available_"       = "No data available";
 "_widget_available_nc25_"   = "Widget only available starting with Nextcloud 25";
 "_keep_running_"            = "Keep the app running for a better user experience";
+"_recent_activity_"         = "Recent activity";
+"_title_lockscreenwidget_"  = "Status";
+"_description_lockscreenwidget_" = "Keep an eye on available space and recent activity";
 
 // Video
 "_select_trace_"            = "Select the trace";

+ 18 - 30
iOSClient/Utility/NCUtility.swift

@@ -994,39 +994,27 @@ class NCUtility: NSObject {
 
         return imagePreview
     }
-    
-    func getWidgetImageUserData(url: URL, fileName: String?, completition: @escaping (_ image: UIImage?) -> () = { _ in }) {
 
-        let size = CGSize(width: 256, height: 256)
-        let options = NKRequestOptions(queue: NKCommon.shared.backgroundQueue)
+    @discardableResult
+    func convertDataToImage(data: Data?, size:CGSize, fileNameToWrite: String?) -> UIImage? {
 
-        if let fileName = fileName {
-            let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
-            if FileManager().fileExists(atPath: fileNamePath) {
-                return completition(UIImage(contentsOfFile: fileNamePath))
-            }
+        guard let data = data else { return nil }
+        var returnImage: UIImage?
+
+        if let image = UIImage(data: data), let image = image.resizeImage(size: size) {
+            returnImage = image
+        } else if let image = SVGKImage(data: data) {
+            image.size = size
+            returnImage = image.uiImage
+        } else {
+            print("error")
         }
-        
-        NextcloudKit.shared.getPreview(url: url, options: options) { account, data, error in
-            var image: UIImage?
-            if error == .success {
-                guard let data = data else { return completition(nil) }
-                if let uiImage = UIImage(data: data) {
-                    image = uiImage.resizeImage(size: size)
-                } else if let svgImage = SVGKImage(data: data) {
-                    svgImage.size = size
-                    image = svgImage.uiImage
-                } else {
-                    print("error")
-                }
-                if let image = image, let fileName = fileName {
-                    do {
-                        let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
-                        try image.pngData()?.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic)
-                    } catch { }
-                }
-            }
-            completition(image)
+        if let fileName = fileNameToWrite, let image = returnImage {
+            do {
+                let fileNamePath: String = CCUtility.getDirectoryUserData() + "/" + fileName + ".png"
+                try image.pngData()?.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic)
+            } catch { }
         }
+        return returnImage
     }
 }