1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

iOS: Add share extension (#4492)

This commit is contained in:
Duncan Cunningham 2021-04-24 10:22:11 +02:00 committed by GitHub
parent e95ea48ce9
commit da8cc73df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 870 additions and 17 deletions

View File

@ -11,13 +11,42 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
46E31F54C547C341F605BB66 /* libPods-Joplin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */; };
5E556FC75AECECB13464A724 /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FAC957496DFD2368FFE3C360 /* libPods-ShareExtension.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
AE152142260F770400217DCB /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AE152141260F770400217DCB /* ShareViewController.m */; };
AE82E4AF2599FA3A0013551B /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE82E4AD2599FA3A0013551B /* MainInterface.storyboard */; };
AE82E4B32599FA3A0013551B /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = AE82E4A82599FA3A0013551B /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
AE82E4B12599FA3A0013551B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = AE82E4A72599FA3A0013551B;
remoteInfo = ShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
AE82E4B42599FA3A0013551B /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
AE82E4B32599FA3A0013551B /* ShareExtension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* JoplinTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JoplinTests.m; sourceTree = "<group>"; };
0473A2D469A3555053E69327 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
09056573D4C040FBD5FEB93A /* Pods-Joplin-JoplinTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-JoplinTests.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-JoplinTests/Pods-Joplin-JoplinTests.debug.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Joplin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Joplin.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Joplin/AppDelegate.h; sourceTree = "<group>"; };
@ -32,13 +61,22 @@
2DA44D9A347489A29B995F73 /* Pods-Joplin-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOSTests/Pods-Joplin-tvOSTests.debug.xcconfig"; sourceTree = "<group>"; };
37DBC181C4AD99CBE0D07EEB /* Pods-Joplin-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOSTests/Pods-Joplin-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
505CB61090817F4453631957 /* Pods-Joplin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.debug.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.debug.xcconfig"; sourceTree = "<group>"; };
5DE39012F71F18423C665C57 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Joplin/LaunchScreen.storyboard; sourceTree = "<group>"; };
A3FEB746EE7F1B0FF28528E1 /* Pods-Joplin-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOS/Pods-Joplin-tvOS.debug.xcconfig"; sourceTree = "<group>"; };
A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
AE152140260F770400217DCB /* ShareViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ShareViewController.h; path = Source/ShareExtension/ShareViewController.h; sourceTree = "<group>"; };
AE152141260F770400217DCB /* ShareViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShareViewController.m; path = Source/ShareExtension/ShareViewController.m; sourceTree = "<group>"; };
AE7945DB259C9A2500051BE2 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
AE7945E6259C9AEE00051BE2 /* Joplin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Joplin.entitlements; path = Joplin/Joplin.entitlements; sourceTree = "<group>"; };
AE82E4A82599FA3A0013551B /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
AE82E4AE2599FA3A0013551B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
AE82E4B02599FA3A0013551B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B61798F36B3BC123BF8EA4D9 /* libPods-Joplin-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
F69B873C692CE22F1C4C9264 /* libPods-Joplin-JoplinTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin-JoplinTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
FAC957496DFD2368FFE3C360 /* libPods-ShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -50,6 +88,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
AE82E4A52599FA3A0013551B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5E556FC75AECECB13464A724 /* libPods-ShareExtension.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -73,6 +119,7 @@
13B07FAE1A68108700A75B9A /* Joplin */ = {
isa = PBXGroup;
children = (
AE7945E6259C9AEE00051BE2 /* Joplin.entitlements */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
@ -93,6 +140,7 @@
258F823D616BE3D6A52BC900 /* libPods-Joplin-tvOSTests.a */,
A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */,
F69B873C692CE22F1C4C9264 /* libPods-Joplin-JoplinTests.a */,
FAC957496DFD2368FFE3C360 /* libPods-ShareExtension.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -110,6 +158,7 @@
13B07FAE1A68108700A75B9A /* Joplin */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* JoplinTests */,
AE82E4A92599FA3A0013551B /* ShareExtension */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
9CDB1D9DB6483D893504BFCB /* Pods */,
@ -123,6 +172,7 @@
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* Joplin.app */,
AE82E4A82599FA3A0013551B /* ShareExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@ -138,10 +188,24 @@
245A6EBAE2E874DB706B16DB /* Pods-Joplin-tvOS.release.xcconfig */,
2DA44D9A347489A29B995F73 /* Pods-Joplin-tvOSTests.debug.xcconfig */,
37DBC181C4AD99CBE0D07EEB /* Pods-Joplin-tvOSTests.release.xcconfig */,
5DE39012F71F18423C665C57 /* Pods-ShareExtension.debug.xcconfig */,
0473A2D469A3555053E69327 /* Pods-ShareExtension.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
AE82E4A92599FA3A0013551B /* ShareExtension */ = {
isa = PBXGroup;
children = (
AE152140260F770400217DCB /* ShareViewController.h */,
AE152141260F770400217DCB /* ShareViewController.m */,
AE7945DB259C9A2500051BE2 /* ShareExtension.entitlements */,
AE82E4AD2599FA3A0013551B /* MainInterface.storyboard */,
AE82E4B02599FA3A0013551B /* Info.plist */,
);
path = ShareExtension;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -156,16 +220,36 @@
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
CBC8354E4CF5CF4E15F2FCDE /* [CP] Copy Pods Resources */,
AE82E4B42599FA3A0013551B /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
AE82E4B22599FA3A0013551B /* PBXTargetDependency */,
);
name = Joplin;
productName = Joplin;
productReference = 13B07F961A680F5B00A75B9A /* Joplin.app */;
productType = "com.apple.product-type.application";
};
AE82E4A72599FA3A0013551B /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = AE82E4B72599FA3A0013551B /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
027E2AA6B101F8CFCA582EC1 /* [CP] Check Pods Manifest.lock */,
AE82E4A42599FA3A0013551B /* Sources */,
AE82E4A52599FA3A0013551B /* Frameworks */,
AE82E4A62599FA3A0013551B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ShareExtension;
productName = ShareExtension;
productReference = AE82E4A82599FA3A0013551B /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -179,6 +263,10 @@
DevelopmentTeam = A9BXAFS6CT;
LastSwiftMigration = 1120;
};
AE82E4A72599FA3A0013551B = {
CreatedOnToolsVersion = 12.0.1;
DevelopmentTeam = A9BXAFS6CT;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Joplin" */;
@ -195,6 +283,7 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* Joplin */,
AE82E4A72599FA3A0013551B /* ShareExtension */,
);
};
/* End PBXProject section */
@ -209,6 +298,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
AE82E4A62599FA3A0013551B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AE82E4AF2599FA3A0013551B /* MainInterface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@ -226,6 +323,28 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=/usr/local/opt/node@12/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
027E2AA6B101F8CFCA582EC1 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
335ACF4DE85695BEBB18D8A3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -329,8 +448,35 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
AE82E4A42599FA3A0013551B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AE152142260F770400217DCB /* ShareViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
AE82E4B22599FA3A0013551B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AE82E4A72599FA3A0013551B /* ShareExtension */;
targetProxy = AE82E4B12599FA3A0013551B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
AE82E4AD2599FA3A0013551B /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
AE82E4AE2599FA3A0013551B /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
@ -338,6 +484,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 63;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
@ -365,6 +512,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 63;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
@ -497,6 +645,62 @@
};
name = Release;
};
AE82E4B52599FA3A0013551B /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5DE39012F71F18423C665C57 /* Pods-ShareExtension.debug.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
AE82E4B62599FA3A0013551B /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0473A2D469A3555053E69327 /* Pods-ShareExtension.release.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -518,6 +722,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AE82E4B72599FA3A0013551B /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AE82E4B52599FA3A0013551B /* Debug */,
AE82E4B62599FA3A0013551B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
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 = "AE82E4A72599FA3A0013551B"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Joplin.app"
BlueprintName = "Joplin"
ReferencedContainer = "container:Joplin.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 = "13B07F861A680F5B00A75B9A"
BuildableName = "Joplin.app"
BlueprintName = "Joplin"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Joplin.app"
BlueprintName = "Joplin"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -3,6 +3,7 @@
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import "RNQuickActionManager.h"
@ -103,4 +104,8 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
#endif
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
return [RCTLinkingManager application:app openURL:url options:options];
}
@end

View File

@ -20,6 +20,19 @@
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>net.cozic.joplin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>joplin</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>

View File

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>group.net.cozic.joplin</string>
</array>
</dict>
</plist>

View File

@ -11,6 +11,7 @@ target 'Joplin' do
use_react_native!(:path => config["reactNativePath"])
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
pod 'JoplinRNShareExtension', :path => 'ShareExtension'
# Enables Flipper.
#
@ -21,3 +22,7 @@ target 'Joplin' do
flipper_post_install(installer)
end
end
target 'ShareExtension' do
pod 'JoplinCommonShareExtension', :path => 'ShareExtension'
end

View File

@ -67,6 +67,10 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- JoplinCommonShareExtension (1.0.0)
- JoplinRNShareExtension (1.0.0):
- JoplinCommonShareExtension
- React (= 0.63.3)
- OpenSSL-Universal (1.0.2.20):
- OpenSSL-Universal/Static (= 1.0.2.20)
- OpenSSL-Universal/Static (1.0.2.20)
@ -373,6 +377,8 @@ DEPENDENCIES:
- FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- JoplinCommonShareExtension (from `ShareExtension`)
- JoplinRNShareExtension (from `ShareExtension`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
@ -444,6 +450,10 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
JoplinCommonShareExtension:
:path: ShareExtension
JoplinRNShareExtension:
:path: ShareExtension
RCTRequired:
:path: "../node_modules/react-native/Libraries/RCTRequired"
RCTTypeSafety:
@ -545,6 +555,8 @@ SPEC CHECKSUMS:
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
JoplinCommonShareExtension: 270b4f8eb4e22828eeda433a04ed689fc1fd09b5
JoplinRNShareExtension: 89e042edc89e290bc88955fe3ac7f1aaea97cbc8
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
@ -590,6 +602,6 @@ SPEC CHECKSUMS:
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: a0d1ca4e385ef46f9103f02206ebf612107dd508
PODFILE CHECKSUM: 6f558aa823c0fd07348253cc01996e4ee3de75c4
COCOAPODS: 1.10.1

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="xea-QC-Aqw">
<rect key="frame" x="197" y="443" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="xea-QC-Aqw" firstAttribute="centerX" secondItem="1Xd-am-t49" secondAttribute="centerX" id="Al3-qb-mVX"/>
<constraint firstItem="xea-QC-Aqw" firstAttribute="centerY" secondItem="1Xd-am-t49" secondAttribute="centerY" id="k5g-vk-GHv"/>
</constraints>
</view>
<connections>
<outlet property="spinner" destination="xea-QC-Aqw" id="qxC-bS-d1N"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="139" y="101"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,47 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>ShareExtension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationDictionaryVersion</key>
<integer>2</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsText</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "JoplinCommonShareExtension"
spec.version = "1.0.0"
spec.summary = "Common Share Extension code for Joplin."
spec.description = "Common Share Extension for Joplin"
spec.homepage = "https://github.com/laurent22/joplin"
spec.license = { :type => "MIT" }
spec.author = { "Duncan Cunningham" => "duncanc4@gmail.com" }
spec.platform = :ios, "9.0"
spec.source = { :path => "." }
spec.source_files = "Source/Common/**/*.{h,m}"
end

View File

@ -0,0 +1,14 @@
Pod::Spec.new do |spec|
spec.name = "JoplinRNShareExtension"
spec.version = "1.0.0"
spec.summary = "React Native module for Joplin to access the data from the share extension."
spec.description = "React Native Share Extension module for Joplin"
spec.homepage = "https://github.com/laurent22/joplin"
spec.license = { :type => "MIT" }
spec.author = { "Duncan Cunningham" => "duncanc4@gmail.com" }
spec.platform = :ios, "9.0"
spec.source = { :path => "." }
spec.source_files = "Source/RNShareExtension/**/*.{h,m}"
spec.dependency "React", "0.63.3"
spec.dependency "JoplinCommonShareExtension"
end

View File

@ -0,0 +1,29 @@
# Share Extension Creation Instructions
## Creating the Target
2. In Xcode select File->New->Target
3. Then search for Share Extension
4. Select Share Extension and click Next
5. In Product Name enter ShareExtension
6. Leave the rest of the options and click Finish
7. If the Activate "ShareExtension" scheme popup comes, click Activate
## Configuring the Share Extension
1. Delete *ShareViewController.h* and *ShareViewController.m* from the project. (Select files and right click, then Delete)
2. On the confirmation popup, select Move to Trash
3. Now add *ShareViewController.h* and *ShareViewController.m* from *ShareExtension/Source/ShareExtenstion*. This can be done by right clicking on the *ShareExtension* folder in Xcode and selecting Add Files to "Joplin". Double check that the ShareExtension is checked for Add to targets and click Add.
4. Switch over to git and reset the changes done to *ShareExtension/Base.lproj/Maininterface.storyboard* and *ShareExtension/Info.plist*, as Xcode generated new versions of these files and overwrote ours.
5. Now select the ShareExtension Target and go to Signing & Capabilities
6. Click the + Capability and search for App Groups and add it
7. Back in git reset *ShareExtension/ShareExtension.entitlements* as Xcode just overwrote it. Back in Xcode you should see the app group set
8. Now switch to General, just left of Signing & Capabilites
9. Under Deployment Info, change the iOS version to match the Joplin target version, which is 9.0
## Configuring Joplin
1. We need to perform the same steps to add the App Group as done for the Share Extension, the only difference is the entitlements files is *Joplin.entitlements*
## Final Steps
1. Run pod install from the ios directory to install the new pods
# Other Information
## App Group
If the app group name needs to be changed for some reason, it needs to be changed for both the Share Extension and Joplin. Also *ShareExtensionGroupIdentifier* in *ShareExtension/Source/Common/ShareExtensionConstants.m* needs to be changed to match the new app group.

View File

@ -0,0 +1,10 @@
<?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>com.apple.security.application-groups</key>
<array>
<string>group.net.cozic.joplin</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,24 @@
//
// ShareData.h
// ShareExtension
//
// Created by Duncan Cunningham on 2/6/21.
// Copyright © 2021 joplinapp.org. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface ShareData : NSObject
@property (nonatomic, strong) NSString* title;
@property (nonatomic, strong) NSString* text;
@property (nonatomic, strong) NSArray<NSDictionary*>* resources;
- (id)initWithDictionary:(NSDictionary*)dictionary;
- (NSDictionary*)encodeToDictionary;
+ (NSDictionary*)resourceDictionaryForURL:(NSString*)url withName:(NSString*)name andMimeType:(NSString*)mimeType;
+ (NSString*)resourceURLFromDictionary:(NSDictionary*)dictionary;
@end

View File

@ -0,0 +1,53 @@
//
// ShareData.m
// ShareExtension
//
// Created by Duncan Cunningham on 2/6/21.
// Copyright © 2021 joplinapp.org. All rights reserved.
//
#import "ShareData.h"
static NSString* const ShareDataTitleKey = @"title";
static NSString* const ShareDataTextKey = @"text";
static NSString* const ShareDataResourcesKey = @"resources";
static NSString* const ShareDataResourceUriKey = @"uri";
static NSString* const ShareDataResourceNameKey = @"name";
static NSString* const ShareDataResourceMimeTypeKey = @"mimeType";
@implementation ShareData
- (id)initWithDictionary:(NSDictionary*)dictionary {
self = [super init];
if (self != nil) {
self.title = [dictionary objectForKey:ShareDataTitleKey];
self.text = [dictionary objectForKey:ShareDataTextKey];
self.resources = [dictionary objectForKey:ShareDataResourcesKey];
}
return self;
}
- (NSDictionary*)encodeToDictionary {
NSString* title = (self.title == nil) ? @"" : self.title;
NSString* text = (self.text == nil) ? @"" : self.text;
NSArray* resources = (self.resources == nil) ? @[] : self.resources;
return @{ShareDataTitleKey: title,
ShareDataTextKey: text,
ShareDataResourcesKey: resources};
}
+ (NSDictionary*)resourceDictionaryForURL:(NSString*)url withName:(NSString*)name andMimeType:(NSString*)mimeType {
return @{ShareDataResourceUriKey: url,
ShareDataResourceNameKey: name,
ShareDataResourceMimeTypeKey: mimeType};
}
+ (NSString*)resourceURLFromDictionary:(NSDictionary*)dictionary {
return [dictionary objectForKey:ShareDataResourceUriKey];
}
@end

View File

@ -0,0 +1,16 @@
//
// ShareExtensionConstants.h
// Joplin
//
// Created by Duncan Cunningham on 2/6/21.
// Copyright © 2021 joplinapp.org. All rights reserved.
//
#ifndef ShareExtensionConstants_h
#define ShareExtensionConstants_h
extern NSString* const ShareExtensionGroupIdentifier;
extern NSString* const ShareExtensionShareURL;
extern NSString* const ShareExtensionShareDataFilename;
#endif /* ShareExtensionConstants_h */

View File

@ -0,0 +1,13 @@
//
// ShareExtensionConstants.m
// Joplin
//
// Created by Duncan Cunningham on 2/6/21.
// Copyright © 2021 joplinapp.org. All rights reserved.
//
#import <Foundation/Foundation.h>
NSString* const ShareExtensionGroupIdentifier = @"group.net.cozic.joplin";
NSString* const ShareExtensionShareURL = @"joplin://shareData";
NSString* const ShareExtensionShareDataFilename = @"shareData";

View File

@ -0,0 +1,67 @@
//
// ShareExtension.m
// Joplin
//
// Created by Duncan Cunningham on 2/6/21.
// Copyright © 2021 joplinapp.org. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "ShareData.h"
#import "ShareExtensionConstants.h"
@interface ShareExtension : NSObject <RCTBridgeModule>
@property (nonatomic, strong) ShareData* shareData;
@end
@implementation ShareExtension
RCT_EXPORT_MODULE();
RCT_REMAP_METHOD(data, resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
NSDictionary* dictionary = [self decodeShareDataFromURL:self.shareDataURL];
self.shareData = [[ShareData alloc] initWithDictionary:dictionary];
resolve(dictionary);
}
RCT_EXPORT_METHOD(close) {
if (self.shareData.resources != nil) {
for (NSDictionary* resource in self.shareData.resources) {
NSString* uri = [ShareData resourceURLFromDictionary:resource];
if (uri != nil && [NSFileManager.defaultManager fileExistsAtPath:uri]) {
[NSFileManager.defaultManager removeItemAtPath:uri error:nil];
}
}
}
[NSFileManager.defaultManager removeItemAtPath:[[self shareDataURL] path] error:nil];
}
+ (BOOL)requiresMainQueueSetup {
return YES;
}
- (NSDictionary*)constantsToExport {
return @{@"SHARE_EXTENSION_SHARE_URL": ShareExtensionShareURL};
}
- (NSDictionary*)decodeShareDataFromURL:(NSURL*)url {
NSData* data = [NSData dataWithContentsOfFile:[url path]];
if (data != nil) {
return [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
} else {
return nil;
}
}
- (NSURL*)shareDataURL {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* sharedContainerURL = [fileManager containerURLForSecurityApplicationGroupIdentifier:ShareExtensionGroupIdentifier];
return [sharedContainerURL URLByAppendingPathComponent:ShareExtensionShareDataFilename];
}
@end

View File

@ -0,0 +1,13 @@
//
// ShareViewController.h
// ShareExtension
//
// Created by Duncan Cunningham on 12/28/20.
// Copyright © 2020 joplinapp.org. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ShareViewController : UIViewController
@end

View File

@ -0,0 +1,143 @@
//
// ShareViewController.m
// ShareExtension
//
// Created by Duncan Cunningham on 12/28/20.
// Copyright © 2020 joplinapp.org. All rights reserved.
//
#import "ShareViewController.h"
#import "ShareData.h"
#import "ShareExtensionConstants.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface ShareViewController ()
@property (nonatomic, strong) NSURL* sharedContainerURL;
@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *spinner;
@end
@implementation ShareViewController
- (NSURL*)sharedContainerURL {
if (_sharedContainerURL == nil) {
NSFileManager* fileManager = [NSFileManager defaultManager];
_sharedContainerURL = [fileManager containerURLForSecurityApplicationGroupIdentifier:ShareExtensionGroupIdentifier];
}
return _sharedContainerURL;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.spinner startAnimating];
[self extractDataFromContext:self.extensionContext withCallback:^(ShareData* shareData) {
[self serializeShareDataToSharedContainer:shareData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
[self.spinner stopAnimating];
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
[self launchMainApp];
});
}];
}
- (void)extractDataFromContext:(NSExtensionContext *)context withCallback:(void(^)(ShareData* shareData))callback {
__block ShareData* shareData = [[ShareData alloc] init];
NSMutableArray* resources = [[NSMutableArray alloc] init];
NSExtensionItem* item = [context.inputItems firstObject];
NSArray* attachments = item.attachments;
__block NSUInteger index = 0;
[attachments enumerateObjectsUsingBlock:^(NSItemProvider* provider, NSUInteger idx, BOOL* stop) {
[provider.registeredTypeIdentifiers enumerateObjectsUsingBlock:^(NSString* identifier, NSUInteger idx, BOOL* stop) {
[provider loadItemForTypeIdentifier:identifier options:nil completionHandler:^(id<NSSecureCoding> item, NSError* error) {
index += 1;
// is an URL - Can be a path or Web URL
if ([(NSObject*)item isKindOfClass:[NSURL class]]) {
NSURL* url = (NSURL*)item;
if ([[url pathExtension] isEqualToString:@""] || [url.scheme containsString:@"http"]) {
shareData.text = [url absoluteString];
} else {
NSURL* copiedURL = [self copyURLToSharedContainer:url];
NSDictionary* resource = [self resourceDictionaryForMediaURL:copiedURL];
[resources addObject:resource];
}
// is a String
} else if ([(NSObject*)item isKindOfClass:[NSString class]]) {
shareData.text = (NSString*)item;
// is an Image
} else if ([(NSObject*)item isKindOfClass:[UIImage class]]) {
UIImage* sharedImage = (UIImage*)item;
NSURL* path = [self copyImageToSharedContainer:sharedImage];
NSDictionary* resource = [self resourceDictionaryForMediaURL:path];
[resources addObject:resource];
}
if (index == [attachments count]) {
shareData.resources = resources;
callback(shareData);
}
}];
// We'll only use the first provider
*stop = YES;
}];
}];
}
- (NSURL*)copyImageToSharedContainer:(UIImage*)image {
NSString* name = [NSString stringWithFormat:@"%@%@", [NSUUID new].UUIDString, @".png"];
NSURL* path = [self.sharedContainerURL URLByAppendingPathComponent:name];
[UIImagePNGRepresentation(image) writeToFile:[path path] atomically:YES];
return path;
}
- (NSURL*)copyURLToSharedContainer:(NSURL*)url {
NSString* name = [url lastPathComponent];
NSURL* path = [self.sharedContainerURL URLByAppendingPathComponent:name];
[[NSFileManager defaultManager] copyItemAtPath:[url path] toPath:[path path] error:nil];
return path;
}
- (void)serializeShareDataToSharedContainer:(ShareData*)shareData {
NSData* data = [NSJSONSerialization dataWithJSONObject:[shareData encodeToDictionary] options:0 error:nil];
NSURL* path = [self.sharedContainerURL URLByAppendingPathComponent:ShareExtensionShareDataFilename isDirectory:NO];
[data writeToFile:[path path] options:NSDataWritingAtomic error: nil];
}
- (NSDictionary*)resourceDictionaryForMediaURL:(NSURL*)url {
NSString* name = [url lastPathComponent];
NSString* extension = [url pathExtension];
NSString* mimeType = [self mimeTypeFor:extension];
return [ShareData resourceDictionaryForURL:[url absoluteString] withName:name andMimeType:mimeType];
}
- (NSString*)mimeTypeFor:(NSString*)fileExtension{
NSString* UTI = (__bridge_transfer NSString*)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString* mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
return mimeType;
}
- (void)launchMainApp {
SEL selector = NSSelectorFromString(@"openURL:");
UIResponder* responder = self;
while (responder != nil) {
if ([responder respondsToSelector:selector]) {
[responder performSelector:selector withObject:[NSURL URLWithString:ShareExtensionShareURL]];
break;
}
responder = responder.nextResponder;
}
}
@end

View File

@ -27,7 +27,7 @@ import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/lo
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar } = require('react-native');
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar, Linking } = require('react-native');
import NetInfo from '@react-native-community/netinfo';
const DropdownAlert = require('react-native-dropdownalert').default;
@ -631,6 +631,12 @@ class AppComponent extends React.Component {
this.onAppStateChange_ = () => {
PoorManIntervals.update();
};
this.handleOpenURL_ = (event: any) => {
if (event.url == ShareExtension.shareURL) {
void this.handleShareData();
}
};
}
// 2020-10-08: It seems the initialisation code is quite fragile in general and should be kept simple.
@ -683,6 +689,8 @@ class AppComponent extends React.Component {
});
}
Linking.addEventListener('url', this.handleOpenURL_);
BackButtonService.initialize(this.backButtonHandler_);
AlarmService.setInAppNotificationHandler(async (alarmId: string) => {
@ -693,21 +701,14 @@ class AppComponent extends React.Component {
AppState.addEventListener('change', this.onAppStateChange_);
const sharedData = await ShareExtension.data();
if (sharedData) {
reg.logger().info('Received shared data');
if (this.props.selectedFolderId) {
await handleShared(sharedData, this.props.selectedFolderId, this.props.dispatch);
} else {
reg.logger().info('Cannot handle share - default folder id is not set');
}
}
await this.handleShareData();
setUpQuickActions(this.props.dispatch, this.props.selectedFolderId);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.onAppStateChange_);
Linking.removeEventListener('url', this.handleOpenURL_);
if (this.unsubscribeNetInfoHandler_) this.unsubscribeNetInfoHandler_();
}
@ -741,6 +742,18 @@ class AppComponent extends React.Component {
return false;
}
async handleShareData() {
const sharedData = await ShareExtension.data();
if (sharedData) {
reg.logger().info('Received shared data');
if (this.props.selectedFolderId) {
await handleShared(sharedData, this.props.selectedFolderId, this.props.dispatch);
} else {
reg.logger().info('Cannot handle share - default folder id is not set');
}
}
}
UNSAFE_componentWillReceiveProps(newProps: any) {
if (newProps.syncStarted != this.lastSyncStarted_) {
if (!newProps.syncStarted) FoldersScreenUtils.refreshFolders();

View File

@ -6,14 +6,16 @@ export interface SharedData {
resources?: string[];
}
const ShareExtension = (Platform.OS === 'android' && NativeModules.ShareExtension) ?
const ShareExtension = (NativeModules.ShareExtension) ?
{
data: () => NativeModules.ShareExtension.data(),
close: () => NativeModules.ShareExtension.close(),
shareURL: (Platform.OS === 'ios') ? NativeModules.ShareExtension.getConstants().SHARE_EXTENSION_SHARE_URL : '',
} :
{
data: () => {},
close: () => {},
shareURL: '',
};
export default ShareExtension;

View File

@ -5,16 +5,20 @@ import Note from '@joplin/lib/models/Note';
import checkPermissions from './checkPermissions.js';
const { ToastAndroid } = require('react-native');
const { PermissionsAndroid } = require('react-native');
const { Platform } = require('react-native');
export default async (sharedData: SharedData, folderId: string, dispatch: Function) => {
if (!!sharedData.resources && sharedData.resources.length > 0) {
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
// No need to check permissions for iOS, the files are already in the shared container
if (Platform.OS === 'android') {
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
if (response !== PermissionsAndroid.RESULTS.GRANTED) {
ToastAndroid.show('Cannot receive shared data - permission denied', ToastAndroid.SHORT);
ShareExtension.close();
return;
if (response !== PermissionsAndroid.RESULTS.GRANTED) {
ToastAndroid.show('Cannot receive shared data - permission denied', ToastAndroid.SHORT);
ShareExtension.close();
return;
}
}
}