From 9a86bb2be3a5f306f92c3ae118c3edee2e36be0b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 29 Mar 2022 15:00:59 +0200 Subject: [PATCH] Add file provider init code Signed-off-by: Claudio Cambra --- .gitignore | 1 + shell_integration/MacOSX/CMakeLists.txt | 16 +- .../FileProviderEnumerator.swift | 64 ++++++ .../FileProviderExt.entitlements | 14 ++ .../FileProviderExtension.swift | 67 ++++++ .../FileProviderExt/FileProviderItem.swift | 52 +++++ .../FileProviderExt/Info.plist | 17 ++ .../FinderSyncExt/Info.plist | 4 +- .../project.pbxproj | 214 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + src/gui/CMakeLists.txt | 3 +- src/gui/fileprovider.h | 35 +++ src/gui/fileprovider_mac.mm | 68 ++++++ src/gui/main.cpp | 5 + 15 files changed, 571 insertions(+), 4 deletions(-) create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderEnumerator.swift create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExt.entitlements create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExtension.swift create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderItem.swift create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/Info.plist create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 src/gui/fileprovider.h create mode 100644 src/gui/fileprovider_mac.mm diff --git a/.gitignore b/.gitignore index 249c4a2c7..85a87ed91 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ dlldata.c # macOS specific xcuserdata/ **/.DS_Store +**/Carthage/ # Visual C++ cache files ipch/ diff --git a/shell_integration/MacOSX/CMakeLists.txt b/shell_integration/MacOSX/CMakeLists.txt index 99116c76d..da91e9c13 100644 --- a/shell_integration/MacOSX/CMakeLists.txt +++ b/shell_integration/MacOSX/CMakeLists.txt @@ -15,12 +15,26 @@ if(APPLE) "OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}" COMMENT building Mac Overlay icons VERBATIM) - add_dependencies(mac_overlayplugin nextcloud) # for the ownCloud.icns to be generated + + add_custom_target( mac_fileproviderplugin ALL + xcodebuild ARCHS=${CMAKE_OSX_ARCHITECTURES} ONLY_ACTIVE_ARCH=NO + -project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj + -target FileProviderExt -configuration Release "SYMROOT=${CMAKE_CURRENT_BINARY_DIR}" + "OC_APPLICATION_NAME=${APPLICATION_NAME}" + "OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}" + "OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}" + COMMENT building macOS File Provider extension + VERBATIM) + + add_dependencies(mac_overlayplugin mac_fileproviderplugin nextcloud) # for the ownCloud.icns to be generated if (BUILD_OWNCLOUD_OSX_BUNDLE) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FinderSyncExt.appex DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns USE_SOURCE_PERMISSIONS) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FileProviderExt.appex + DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns + USE_SOURCE_PERMISSIONS) endif() endif() diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderEnumerator.swift b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderEnumerator.swift new file mode 100644 index 000000000..9cd356396 --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderEnumerator.swift @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * 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 2 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. + */ + +import FileProvider + +class FileProviderEnumerator: NSObject, NSFileProviderEnumerator { + + private let enumeratedItemIdentifier: NSFileProviderItemIdentifier + private let anchor = NSFileProviderSyncAnchor("an anchor".data(using: .utf8)!) + + init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) { + self.enumeratedItemIdentifier = enumeratedItemIdentifier + super.init() + } + + func invalidate() { + // TODO: perform invalidation of server connection if necessary + } + + func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) { + /* TODO: + - inspect the page to determine whether this is an initial or a follow-up request + + If this is an enumerator for a directory, the root container or all directories: + - perform a server request to fetch directory contents + If this is an enumerator for the active set: + - perform a server request to update your local database + - fetch the active set from your local database + + - inform the observer about the items returned by the server (possibly multiple times) + - inform the observer that you are finished with this page + */ + observer.didEnumerate([FileProviderItem(identifier: NSFileProviderItemIdentifier("a file"))]) + observer.finishEnumerating(upTo: nil) + } + + func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) { + /* TODO: + - query the server for updates since the passed-in sync anchor + + If this is an enumerator for the active set: + - note the changes in your local database + + - inform the observer about item deletions and updates (modifications + insertions) + - inform the observer when you have finished enumerating up to a subsequent sync anchor + */ + observer.finishEnumeratingChanges(upTo: anchor, moreComing: false) + } + + func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) { + completionHandler(anchor) + } +} diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExt.entitlements b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExt.entitlements new file mode 100644 index 000000000..f3997007c --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExt.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.application-identifier + $(AppIdentifierPrefix)$(PRODUCT_BUNDLE_IDENTIFIER) + com.apple.security.app-sandbox + + com.apple.security.application-groups + + $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + + + diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExtension.swift new file mode 100644 index 000000000..df2f2e879 --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderExtension.swift @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * 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 2 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. + */ + +import FileProvider + +class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension { + required init(domain: NSFileProviderDomain) { + // TODO: The containing application must create a domain using `NSFileProviderManager.add(_:, completionHandler:)`. The system will then launch the application extension process, call `FileProviderExtension.init(domain:)` to instantiate the extension for that domain, and call methods on the instance. + super.init() + } + + func invalidate() { + // TODO: cleanup any resources + } + + func item(for identifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress { + // resolve the given identifier to a record in the model + + // TODO: implement the actual lookup + + completionHandler(FileProviderItem(identifier: identifier), nil) + return Progress() + } + + func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier, version requestedVersion: NSFileProviderItemVersion?, request: NSFileProviderRequest, completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress { + // TODO: implement fetching of the contents for the itemIdentifier at the specified version + + completionHandler(nil, nil, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func createItem(basedOn itemTemplate: NSFileProviderItem, fields: NSFileProviderItemFields, contents url: URL?, options: NSFileProviderCreateItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress { + // TODO: a new item was created on disk, process the item's creation + + completionHandler(itemTemplate, [], false, nil) + return Progress() + } + + func modifyItem(_ item: NSFileProviderItem, baseVersion version: NSFileProviderItemVersion, changedFields: NSFileProviderItemFields, contents newContents: URL?, options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress { + // TODO: an item was modified on disk, process the item's modification + + completionHandler(nil, [], false, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func deleteItem(identifier: NSFileProviderItemIdentifier, baseVersion version: NSFileProviderItemVersion, options: NSFileProviderDeleteItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (Error?) -> Void) -> Progress { + // TODO: an item was deleted on disk, process the item's deletion + + completionHandler(NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest) throws -> NSFileProviderEnumerator { + return FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier) + } +} diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderItem.swift b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderItem.swift new file mode 100644 index 000000000..fa793e42b --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/FileProviderItem.swift @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * 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 2 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. + */ + +import FileProvider +import UniformTypeIdentifiers + +class FileProviderItem: NSObject, NSFileProviderItem { + + // TODO: implement an initializer to create an item from your extension's backing model + // TODO: implement the accessors to return the values from your extension's backing model + + private let identifier: NSFileProviderItemIdentifier + + init(identifier: NSFileProviderItemIdentifier) { + self.identifier = identifier + } + + var itemIdentifier: NSFileProviderItemIdentifier { + return identifier + } + + var parentItemIdentifier: NSFileProviderItemIdentifier { + return .rootContainer + } + + var capabilities: NSFileProviderItemCapabilities { + return [.allowsReading, .allowsWriting, .allowsRenaming, .allowsReparenting, .allowsTrashing, .allowsDeleting] + } + + var itemVersion: NSFileProviderItemVersion { + NSFileProviderItemVersion(contentVersion: "a content version".data(using: .utf8)!, metadataVersion: "a metadata version".data(using: .utf8)!) + } + + var filename: String { + return identifier.rawValue + } + + var contentType: UTType { + return identifier == NSFileProviderItemIdentifier.rootContainer ? .folder : .plainText + } +} diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/Info.plist b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/Info.plist new file mode 100644 index 000000000..5072333ec --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FileProviderExt/Info.plist @@ -0,0 +1,17 @@ + + + + + NSExtension + + NSExtensionFileProviderDocumentGroup + $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + NSExtensionFileProviderSupportsEnumeration + + NSExtensionPointIdentifier + com.apple.fileprovider-nonui + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).FileProviderExtension + + + diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/Info.plist b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/Info.plist index 88bb87e91..2a669ce73 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/Info.plist +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/Info.plist @@ -2,8 +2,6 @@ - SocketApiPrefix - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) CFBundleDevelopmentRegion en CFBundleDisplayName @@ -39,5 +37,7 @@ NSPrincipalClass NSApplication + SocketApiPrefix + $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj index ad6d849dc..f15453b20 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 538E396A27F4765000FA63D5 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */; }; + 538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */; }; + 538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396E27F4765000FA63D5 /* FileProviderItem.swift */; }; + 538E397127F4765000FA63D5 /* FileProviderEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E397027F4765000FA63D5 /* FileProviderEnumerator.swift */; }; + 538E397627F4765000FA63D5 /* FileProviderExt.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 538E396727F4765000FA63D5 /* FileProviderExt.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 539158AC27BE71A900816F56 /* LineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* LineProcessor.m */; }; 539158B327BEC98A00816F56 /* LocalSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158B227BEC98A00816F56 /* LocalSocketClient.m */; }; C2B573BA1B1CD91E00303B36 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; }; @@ -21,6 +26,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 538E397427F4765000FA63D5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2B573951B1CD88000303B36 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 538E396627F4765000FA63D5; + remoteInfo = FileProviderExt; + }; C2B573DF1B1CD9CE00303B36 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C2B573951B1CD88000303B36 /* Project object */; @@ -38,6 +50,7 @@ dstSubfolderSpec = 13; files = ( C2B573E21B1CD9CE00303B36 /* FinderSyncExt.appex in Embed App Extensions */, + 538E397627F4765000FA63D5 /* FileProviderExt.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 1; @@ -45,6 +58,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 538E396727F4765000FA63D5 /* FileProviderExt.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FileProviderExt.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; }; + 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderExtension.swift; sourceTree = ""; }; + 538E396E27F4765000FA63D5 /* FileProviderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderItem.swift; sourceTree = ""; }; + 538E397027F4765000FA63D5 /* FileProviderEnumerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderEnumerator.swift; sourceTree = ""; }; + 538E397227F4765000FA63D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 538E397327F4765000FA63D5 /* FileProviderExt.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FileProviderExt.entitlements; sourceTree = ""; }; 539158A927BE606500816F56 /* LineProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LineProcessor.h; sourceTree = ""; }; 539158AA27BE67CC00816F56 /* SyncClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SyncClient.h; sourceTree = ""; }; 539158AB27BE71A900816F56 /* LineProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LineProcessor.m; sourceTree = ""; }; @@ -66,6 +86,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 538E396427F4765000FA63D5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 538E396A27F4765000FA63D5 /* UniformTypeIdentifiers.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C2B573AE1B1CD91E00303B36 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -83,11 +111,33 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 538E396827F4765000FA63D5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 538E396B27F4765000FA63D5 /* FileProviderExt */ = { + isa = PBXGroup; + children = ( + 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */, + 538E396E27F4765000FA63D5 /* FileProviderItem.swift */, + 538E397027F4765000FA63D5 /* FileProviderEnumerator.swift */, + 538E397227F4765000FA63D5 /* Info.plist */, + 538E397327F4765000FA63D5 /* FileProviderExt.entitlements */, + ); + path = FileProviderExt; + sourceTree = ""; + }; C2B573941B1CD88000303B36 = { isa = PBXGroup; children = ( C2B573B31B1CD91E00303B36 /* desktopclient */, C2B573D81B1CD9CE00303B36 /* FinderSyncExt */, + 538E396B27F4765000FA63D5 /* FileProviderExt */, + 538E396827F4765000FA63D5 /* Frameworks */, C2B573B21B1CD91E00303B36 /* Products */, ); sourceTree = ""; @@ -97,6 +147,7 @@ children = ( C2B573B11B1CD91E00303B36 /* desktopclient.app */, C2B573D71B1CD9CE00303B36 /* FinderSyncExt.appex */, + 538E396727F4765000FA63D5 /* FileProviderExt.appex */, ); name = Products; sourceTree = ""; @@ -150,6 +201,25 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 538E396627F4765000FA63D5 /* FileProviderExt */ = { + isa = PBXNativeTarget; + buildConfigurationList = 538E397927F4765000FA63D5 /* Build configuration list for PBXNativeTarget "FileProviderExt" */; + buildPhases = ( + 538E396327F4765000FA63D5 /* Sources */, + 538E396427F4765000FA63D5 /* Frameworks */, + 538E396527F4765000FA63D5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FileProviderExt; + packageProductDependencies = ( + ); + productName = FileProviderExt; + productReference = 538E396727F4765000FA63D5 /* FileProviderExt.appex */; + productType = "com.apple.product-type.app-extension"; + }; C2B573B01B1CD91E00303B36 /* desktopclient */ = { isa = PBXNativeTarget; buildConfigurationList = C2B573CC1B1CD91E00303B36 /* Build configuration list for PBXNativeTarget "desktopclient" */; @@ -163,6 +233,7 @@ ); dependencies = ( C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */, + 538E397527F4765000FA63D5 /* PBXTargetDependency */, ); name = desktopclient; productName = desktopclient; @@ -193,8 +264,13 @@ C2B573951B1CD88000303B36 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1330; LastUpgradeCheck = 1240; TargetAttributes = { + 538E396627F4765000FA63D5 = { + CreatedOnToolsVersion = 13.3; + ProvisioningStyle = Manual; + }; C2B573B01B1CD91E00303B36 = { CreatedOnToolsVersion = 6.3.1; DevelopmentTeam = 9B5WD74GWJ; @@ -222,17 +298,27 @@ Base, ); mainGroup = C2B573941B1CD88000303B36; + packageReferences = ( + ); productRefGroup = C2B573B21B1CD91E00303B36 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C2B573B01B1CD91E00303B36 /* desktopclient */, C2B573D61B1CD9CE00303B36 /* FinderSyncExt */, + 538E396627F4765000FA63D5 /* FileProviderExt */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 538E396527F4765000FA63D5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C2B573AF1B1CD91E00303B36 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -273,6 +359,16 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 538E396327F4765000FA63D5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */, + 538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */, + 538E397127F4765000FA63D5 /* FileProviderEnumerator.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C2B573AD1B1CD91E00303B36 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -294,6 +390,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 538E397527F4765000FA63D5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 538E396627F4765000FA63D5 /* FileProviderExt */; + targetProxy = 538E397427F4765000FA63D5 /* PBXContainerItemProxy */; + }; C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C2B573D61B1CD9CE00303B36 /* FinderSyncExt */; @@ -302,6 +403,103 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 538E397727F4765000FA63D5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FileProviderExt/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FileProviderExt; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.desktopclient.FileProviderExt; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 538E397827F4765000FA63D5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FileProviderExt/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FileProviderExt; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.desktopclient.FileProviderExt; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; C2B573991B1CD88000303B36 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -370,6 +568,7 @@ C2B573CD1B1CD91E00303B36 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -414,12 +613,16 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; C2B573CE1B1CD91E00303B36 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -457,6 +660,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -567,6 +772,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 538E397927F4765000FA63D5 /* Build configuration list for PBXNativeTarget "FileProviderExt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 538E397727F4765000FA63D5 /* Debug */, + 538E397827F4765000FA63D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C2B573981B1CD88000303B36 /* Build configuration list for PBXProject "OwnCloudFinderSync" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 7639f95ab..5c4904d11 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -280,6 +280,7 @@ endif() IF( APPLE ) list(APPEND client_SRCS cocoainitializer_mac.mm) list(APPEND client_SRCS systray.mm) + list(APPEND client_SRCS fileprovider_mac.mm) if(SPARKLE_FOUND AND BUILD_UPDATER) # Define this, we need to check in updater.cpp @@ -653,7 +654,7 @@ endif() if (APPLE) find_package(Qt5 COMPONENTS MacExtras) - target_link_libraries(nextcloudCore PUBLIC Qt5::MacExtras "-framework UserNotifications") + target_link_libraries(nextcloudCore PUBLIC Qt5::MacExtras "-framework UserNotifications FileProvider") endif() if(WITH_CRASHREPORTER) diff --git a/src/gui/fileprovider.h b/src/gui/fileprovider.h new file mode 100644 index 000000000..0c816fe3e --- /dev/null +++ b/src/gui/fileprovider.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) by Claudio Cambra + * + * 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 2 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. + */ + +#ifndef FILEPROVIDER_H +#define FILEPROVIDER_H + +namespace OCC { +namespace Mac { + + class FileProviderInitializer + { + public: + FileProviderInitializer(); + ~FileProviderInitializer(); + + private: + class Private; + Private *d; + }; + +} // namespace Mac +} // namespace OCC + +#endif diff --git a/src/gui/fileprovider_mac.mm b/src/gui/fileprovider_mac.mm new file mode 100644 index 000000000..59a706e20 --- /dev/null +++ b/src/gui/fileprovider_mac.mm @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * 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 2 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. + */ + + +#import +#import + +#include "application.h" +#include "fileprovider.h" + +namespace OCC { +namespace Mac { + +class FileProviderInitializer::Private { + public: + Private() { + domainIdentifier = @APPLICATION_REV_DOMAIN; + name = @APPLICATION_NAME; + fileProviderDomain = [[NSFileProviderDomain alloc] initWithIdentifier:domainIdentifier displayName:name]; + setupFileProvider(); + } + + ~Private() = default; + + void setupFileProvider() { + [NSFileProviderManager addDomain:fileProviderDomain completionHandler:^(NSError *error) { + if(error) { + NSLog(@"Add file provider domain: %i %@", [error code], [error localizedDescription]); + } + }]; + } + + void removeFileProvider() { + [NSFileProviderManager removeDomain:fileProviderDomain completionHandler:^(NSError *error) { + if(error) { + NSLog(@"Remove file provider domain: %i %@", [error code], [error localizedDescription]); + } + }]; + } + + NSFileProviderDomainIdentifier domainIdentifier; + NSString *name; + NSFileProviderDomain *fileProviderDomain; +}; + +FileProviderInitializer::FileProviderInitializer() { + d = new FileProviderInitializer::Private(); + d->setupFileProvider(); +} + +FileProviderInitializer::~FileProviderInitializer() { + d->removeFileProvider(); + delete d; +} + +} // namespace Mac +} // namespace OCC diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 40af80ac0..3b746d645 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -22,6 +22,10 @@ #include #endif +#ifdef Q_OS_MACOS +#include "fileprovider.h" +#endif + #include "application.h" #include "cocoainitializer.h" #include "theme.h" @@ -74,6 +78,7 @@ int main(int argc, char **argv) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); #ifdef Q_OS_MAC Mac::CocoaInitializer cocoaInit; // RIIA + Mac::FileProviderInitializer fileProviderInit; #endif auto surfaceFormat = QSurfaceFormat::defaultFormat();