diff --git a/android/src/main/java/chat/rocket/RealPathUtil.java b/android/src/main/java/chat/rocket/RealPathUtil.java index 97f315fe..5b1f443a 100644 --- a/android/src/main/java/chat/rocket/RealPathUtil.java +++ b/android/src/main/java/chat/rocket/RealPathUtil.java @@ -112,11 +112,17 @@ public static String getPathFromSavingTempFile(Context context, final Uri uri) { ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r"); - FileChannel src = new FileInputStream(pfd.getFileDescriptor()).getChannel(); - FileChannel dst = new FileOutputStream(tmpFile).getChannel(); - dst.transferFrom(src, 0, src.size()); - src.close(); - dst.close(); + InputStream fileStream = new FileInputStream(pfd.getFileDescriptor()); + OutputStream newDatabase = new FileOutputStream(tmpFile); + byte[] buffer = new byte[1024]; + int length; + while((length = fileStream.read(buffer)) > 0) + { + newDatabase.write(buffer, 0, length); + } + newDatabase.flush(); + fileStream.close(); + newDatabase.close(); } catch (IOException ex) { return null; } @@ -199,4 +205,4 @@ public static String getMimeTypeFromUri(final Context context, final Uri uri) { return "application/octet-stream"; } } -} \ No newline at end of file +} diff --git a/android/src/main/java/chat/rocket/ShareModule.java b/android/src/main/java/chat/rocket/ShareModule.java index f9f40079..ba8fdc42 100644 --- a/android/src/main/java/chat/rocket/ShareModule.java +++ b/android/src/main/java/chat/rocket/ShareModule.java @@ -86,8 +86,11 @@ public WritableMap processIntent() { } else if (type.equals("video/*")) { type = "video/mp4"; } - - map.putString("type", "media"); + if(type.equals("text/x-vcard")) { + map.putString("type", "contact"); + } else { + map.putString("type", "media"); + } } } } diff --git a/ios/ContactsHelper.h b/ios/ContactsHelper.h new file mode 100644 index 00000000..cb38f537 --- /dev/null +++ b/ios/ContactsHelper.h @@ -0,0 +1,9 @@ +#import +#import + +@interface ContactsHelper : NSObject +- (NSDictionary *) contactToDictionary:(CNContact *)person withThumbnails:(BOOL)withThumbnails API_AVAILABLE(ios(9.0)); +- (NSString *) getPathForDirectory:(int)directory; +- (NSString *) thumbnailFilePath:(NSString *)recordID; +- (NSString *) getFilePathForThumbnailImage:(CNContact *)contact recordID:(NSString *)recordID API_AVAILABLE(ios(9.0)); +@end diff --git a/ios/ContactsHelper.m b/ios/ContactsHelper.m new file mode 100644 index 00000000..447da6e5 --- /dev/null +++ b/ios/ContactsHelper.m @@ -0,0 +1,203 @@ +#import + +@implementation ContactsHelper : NSObject + +- (NSDictionary*)contactToDictionary:(CNContact *)person withThumbnails:(BOOL)withThumbnails API_AVAILABLE(ios(9.0)) { + NSMutableDictionary* output = [NSMutableDictionary dictionary]; + + NSString *recordID = person.identifier; + NSString *givenName = person.givenName; + NSString *familyName = person.familyName; + NSString *middleName = person.middleName; + NSString *company = person.organizationName; + NSString *jobTitle = person.jobTitle; + NSDateComponents *birthday = person.birthday; + + [output setObject:recordID forKey: @"recordID"]; + + if (givenName) { + [output setObject: (givenName) ? givenName : @"" forKey:@"givenName"]; + } + + if (familyName) { + [output setObject: (familyName) ? familyName : @"" forKey:@"familyName"]; + } + + if(middleName){ + [output setObject: (middleName) ? middleName : @"" forKey:@"middleName"]; + } + + if(company){ + [output setObject: (company) ? company : @"" forKey:@"company"]; + } + + if(jobTitle){ + [output setObject: (jobTitle) ? jobTitle : @"" forKey:@"jobTitle"]; + } + + if (birthday) { + if (birthday.month != NSDateComponentUndefined && birthday.day != NSDateComponentUndefined) { + //months are indexed to 0 in JavaScript (0 = January) so we subtract 1 from NSDateComponents.month + if (birthday.year != NSDateComponentUndefined) { + [output setObject:@{@"year": @(birthday.year), @"month": @(birthday.month - 1), @"day": @(birthday.day)} forKey:@"birthday"]; + } else { + [output setObject:@{@"month": @(birthday.month - 1), @"day":@(birthday.day)} forKey:@"birthday"]; + } + } + } + + //handle phone numbers + NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init]; + + for (CNLabeledValue* labeledValue in person.phoneNumbers) { + NSMutableDictionary* phone = [NSMutableDictionary dictionary]; + NSString * label = [CNLabeledValue localizedStringForLabel:[labeledValue label]]; + NSString* value = [[labeledValue value] stringValue]; + + if(value) { + if(!label) { + label = [CNLabeledValue localizedStringForLabel:@"other"]; + } + [phone setObject: value forKey:@"number"]; + [phone setObject: label forKey:@"label"]; + [phoneNumbers addObject:phone]; + } + } + + [output setObject: phoneNumbers forKey:@"phoneNumbers"]; + //end phone numbers + + //handle urls + NSMutableArray *urlAddresses = [[NSMutableArray alloc] init]; + + for (CNLabeledValue* labeledValue in person.urlAddresses) { + NSMutableDictionary* url = [NSMutableDictionary dictionary]; + NSString* label = [CNLabeledValue localizedStringForLabel:[labeledValue label]]; + NSString* value = [labeledValue value]; + + if(value) { + if(!label) { + label = [CNLabeledValue localizedStringForLabel:@"home"]; + } + [url setObject: value forKey:@"url"]; + [url setObject: label forKey:@"label"]; + [urlAddresses addObject:url]; + } else { + NSLog(@"ignoring blank url"); + } + } + + [output setObject: urlAddresses forKey:@"urlAddresses"]; + + //end urls + + //handle emails + NSMutableArray *emailAddreses = [[NSMutableArray alloc] init]; + + for (CNLabeledValue* labeledValue in person.emailAddresses) { + NSMutableDictionary* email = [NSMutableDictionary dictionary]; + NSString* label = [CNLabeledValue localizedStringForLabel:[labeledValue label]]; + NSString* value = [labeledValue value]; + + if(value) { + if(!label) { + label = [CNLabeledValue localizedStringForLabel:@"other"]; + } + [email setObject: value forKey:@"email"]; + [email setObject: label forKey:@"label"]; + [emailAddreses addObject:email]; + } + } + + [output setObject: emailAddreses forKey:@"emailAddresses"]; + //end emails + + //handle postal addresses + NSMutableArray *postalAddresses = [[NSMutableArray alloc] init]; + + for (CNLabeledValue* labeledValue in person.postalAddresses) { + CNPostalAddress* postalAddress = labeledValue.value; + NSMutableDictionary* address = [NSMutableDictionary dictionary]; + + NSString* street = postalAddress.street; + if(street){ + [address setObject:street forKey:@"street"]; + } + NSString* city = postalAddress.city; + if(city){ + [address setObject:city forKey:@"city"]; + } + NSString* state = postalAddress.state; + if(state){ + [address setObject:state forKey:@"state"]; + } + NSString* region = postalAddress.state; + if(region){ + [address setObject:region forKey:@"region"]; + } + NSString* postCode = postalAddress.postalCode; + if(postCode){ + [address setObject:postCode forKey:@"postCode"]; + } + NSString* country = postalAddress.country; + if(country){ + [address setObject:country forKey:@"country"]; + } + + NSString* label = [CNLabeledValue localizedStringForLabel:labeledValue.label]; + if(label) { + [address setObject:label forKey:@"label"]; + + [postalAddresses addObject:address]; + } + } + + [output setObject:postalAddresses forKey:@"postalAddresses"]; + //end postal addresses + + [output setValue:[NSNumber numberWithBool:person.imageDataAvailable] forKey:@"hasThumbnail"]; + if (withThumbnails) { + [output setObject:[self getFilePathForThumbnailImage:person recordID:recordID] forKey:@"thumbnailPath"]; + } + + return output; +} + +- (NSString *)getPathForDirectory:(int)directory { + NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES); + return [paths firstObject]; +} + +- (NSString *)thumbnailFilePath:(NSString *)recordID { + NSString *filename = [recordID stringByReplacingOccurrencesOfString:@":ABPerson" withString:@""]; + NSString* filepath = [NSString stringWithFormat:@"%@/rnextensionshare_%@.png", [self getPathForDirectory:NSCachesDirectory], filename]; + return filepath; +} + +- (NSString *)getFilePathForThumbnailImage:(CNContact *)contact recordID:(NSString *)recordID API_AVAILABLE(ios(9.0)) { + if (contact.imageDataAvailable){ + NSString *filepath = [self thumbnailFilePath:recordID]; + NSData *contactImageData = contact.thumbnailImageData; + + if ([[NSFileManager defaultManager] fileExistsAtPath:filepath]) { + NSData *existingImageData = [NSData dataWithContentsOfFile: filepath]; + + if([contactImageData isEqual: existingImageData]) { + return filepath; + } + } + + BOOL success = [[NSFileManager defaultManager] createFileAtPath:filepath contents:contactImageData attributes:nil]; + + if (!success) { + NSLog(@"Unable to copy image"); + return @""; + } + + return filepath; + } + + return @""; +} + +@end diff --git a/ios/ReactNativeShareExtension.m b/ios/ReactNativeShareExtension.m index c1e0fc73..51cb86a8 100644 --- a/ios/ReactNativeShareExtension.m +++ b/ios/ReactNativeShareExtension.m @@ -1,3 +1,5 @@ +#import + #import #import @@ -6,6 +8,7 @@ #endif #import "ReactNativeShareExtension.h" +#import "ContactsHelper.h" NSExtensionContext* extensionContext; @@ -80,12 +83,22 @@ - (void)extractDataFromContext:(NSExtensionContext *)context withCallback:(void( // is an URL - Can be a path or Web URL if ([(NSObject *)item isKindOfClass:[NSURL class]]) { - NSURL *url = (NSURL *) item; + NSURL *url = (NSURL *)item; string = [url absoluteString]; type = ([[string pathExtension] isEqualToString:@""]) || [url.scheme containsString:@"http"] ? @"text" : @"media"; [data addObject:@{ @"value": string, @"type": type }]; - + + // is a Dictionary + } else if ([(NSObject *)item isKindOfClass:[NSData class]]) { + if (@available(iOS 9.0, *)) { + NSArray *contacts = [CNContactVCardSerialization contactsWithData:(NSData *)item error:nil]; + for (CNContact *contact in contacts) { + type = @"contact"; + [data addObject:@{ @"value": [[ContactsHelper new] contactToDictionary:contact withThumbnails:true], @"type": type }]; + } + } + // is a String } else if ([(NSObject *)item isKindOfClass:[NSString class]]) { string = (NSString *)item; diff --git a/ios/ReactNativeShareExtension.xcodeproj/project.pbxproj b/ios/ReactNativeShareExtension.xcodeproj/project.pbxproj index dfd59076..8bf4e7ca 100644 --- a/ios/ReactNativeShareExtension.xcodeproj/project.pbxproj +++ b/ios/ReactNativeShareExtension.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 1E908633243572E7005708C0 /* ContactsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E908632243572E7005708C0 /* ContactsHelper.m */; }; 41B5DE3E1D0B50D300949BD5 /* ReactNativeShareExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B5DE3D1D0B50D300949BD5 /* ReactNativeShareExtension.m */; }; /* End PBXBuildFile section */ @@ -23,6 +24,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1E908631243572D9005708C0 /* ContactsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContactsHelper.h; sourceTree = ""; }; + 1E908632243572E7005708C0 /* ContactsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContactsHelper.m; sourceTree = ""; }; 41B5DE301D0B505800949BD5 /* libReactNativeShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactNativeShareExtension.a; sourceTree = BUILT_PRODUCTS_DIR; }; 41B5DE3D1D0B50D300949BD5 /* ReactNativeShareExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativeShareExtension.m; sourceTree = ""; }; 41B5DE3F1D0B50FA00949BD5 /* ReactNativeShareExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactNativeShareExtension.h; sourceTree = ""; }; @@ -58,6 +61,8 @@ 41B5DE3C1D0B506B00949BD5 /* src */ = { isa = PBXGroup; children = ( + 1E908632243572E7005708C0 /* ContactsHelper.m */, + 1E908631243572D9005708C0 /* ContactsHelper.h */, 41B5DE3F1D0B50FA00949BD5 /* ReactNativeShareExtension.h */, 41B5DE3D1D0B50D300949BD5 /* ReactNativeShareExtension.m */, ); @@ -103,6 +108,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 41B5DE271D0B505800949BD5; @@ -120,6 +126,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1E908633243572E7005708C0 /* ContactsHelper.m in Sources */, 41B5DE3E1D0B50D300949BD5 /* ReactNativeShareExtension.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -262,6 +269,7 @@ 41B5DE3B1D0B505800949BD5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcuserdata/djorkaeff.xcuserdatad/UserInterfaceState.xcuserstate b/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcuserdata/djorkaeff.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..b154c6ed Binary files /dev/null and b/ios/ReactNativeShareExtension.xcodeproj/project.xcworkspace/xcuserdata/djorkaeff.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/ReactNativeShareExtension.xcodeproj/xcuserdata/djorkaeff.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/ReactNativeShareExtension.xcodeproj/xcuserdata/djorkaeff.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..6349a757 --- /dev/null +++ b/ios/ReactNativeShareExtension.xcodeproj/xcuserdata/djorkaeff.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + ReactNativeShareExtension.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/package.json b/package.json index 38d9aa2b..eb66d24d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rn-extensions-share", - "version": "2.3.10", + "version": "2.4.0-rc.1", "description": "Share-Extension using react-native for both ios and android", "main": "lib/index.js", "scripts": {