feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
@@ -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>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<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>$(CURRENT_PROJECT_VERSION)</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>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -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 => "AGPL-3.0-or-later" }
|
||||
spec.author = { "Duncan Cunningham" => "duncanc4@gmail.com" }
|
||||
spec.platform = :ios, "9.0"
|
||||
spec.source = { :path => "." }
|
||||
spec.source_files = "Source/Common/**/*.{h,m}"
|
||||
end
|
||||
@@ -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 => "AGPL-3.0-or-later" }
|
||||
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"
|
||||
spec.dependency "JoplinCommonShareExtension"
|
||||
end
|
||||
@@ -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/ShareExtension*. 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 & Capabilities
|
||||
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.
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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";
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// 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]) {
|
||||
UIApplication *app = (UIApplication*) responder;
|
||||
NSURL *url = [NSURL URLWithString:ShareExtensionShareURL];
|
||||
[app openURL:url options:@{} completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
NSLog(@"Opened URL successfully!");
|
||||
} else {
|
||||
NSLog(@"Failed to open URL.");
|
||||
}
|
||||
}];
|
||||
break;
|
||||
}
|
||||
|
||||
responder = responder.nextResponder;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
在新工单中引用
屏蔽一个用户