community-cordova-plugin-social-sharing 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,861 @@
1
+ #import "SocialSharing.h"
2
+ #import "NSString+SSURLEncoding.h"
3
+ #import <Cordova/CDV.h>
4
+ #import <Social/Social.h>
5
+ #import <Foundation/NSException.h>
6
+ #import <MessageUI/MFMessageComposeViewController.h>
7
+ #import <MessageUI/MFMailComposeViewController.h>
8
+ #import <MobileCoreServices/MobileCoreServices.h>
9
+
10
+ static NSString *const kShareOptionMessage = @"message";
11
+ static NSString *const kShareOptionSubject = @"subject";
12
+ static NSString *const kShareOptionFiles = @"files";
13
+ static NSString *const kShareOptionUrl = @"url";
14
+ static NSString *const kShareOptionIPadCoordinates = @"iPadCoordinates";
15
+
16
+ @implementation SocialSharing {
17
+ UIPopoverController *_popover;
18
+ NSString *_popupCoordinates;
19
+ }
20
+
21
+ - (void)pluginInitialize {
22
+ }
23
+
24
+ - (void)available:(CDVInvokedUrlCommand*)command {
25
+ BOOL avail = NO;
26
+ if (NSClassFromString(@"UIActivityViewController")) {
27
+ avail = YES;
28
+ }
29
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail];
30
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
31
+ }
32
+
33
+ - (NSString*)getIPadPopupCoordinates {
34
+ // see https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/issues/1052
35
+ return nil;
36
+ }
37
+
38
+ - (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command {
39
+ _popupCoordinates = [command.arguments objectAtIndex:0];
40
+ }
41
+
42
+ - (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps {
43
+ CGRect rect = CGRectZero;
44
+ if ([comps count] == 4) {
45
+ rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]);
46
+ }
47
+ return rect;
48
+ }
49
+
50
+ - (void)share:(CDVInvokedUrlCommand*)command {
51
+ [self shareInternal:command
52
+ withOptions:@{
53
+ kShareOptionMessage: [command.arguments objectAtIndex:0],
54
+ kShareOptionSubject: [command.arguments objectAtIndex:1],
55
+ kShareOptionFiles: [command.arguments objectAtIndex:2],
56
+ kShareOptionUrl: [command.arguments objectAtIndex:3],
57
+ kShareOptionIPadCoordinates: [command.arguments objectAtIndex:4]
58
+ }
59
+ isBooleanResponse:YES
60
+ ];
61
+ }
62
+
63
+ - (void)shareWithOptions:(CDVInvokedUrlCommand*)command {
64
+ NSDictionary* options = [command.arguments objectAtIndex:0];
65
+ [self shareInternal:command
66
+ withOptions:options
67
+ isBooleanResponse:NO
68
+ ];
69
+ }
70
+
71
+ - (void)shareInternal:(CDVInvokedUrlCommand*)command withOptions:(NSDictionary*)options isBooleanResponse:(BOOL)boolResponse {
72
+ if (!NSClassFromString(@"UIActivityViewController")) {
73
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
74
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
75
+ return;
76
+ }
77
+
78
+ NSString *message = options[kShareOptionMessage];
79
+ NSString *subject = options[kShareOptionSubject];
80
+ NSArray *filenames = options[kShareOptionFiles];
81
+ NSString *urlString = options[kShareOptionUrl];
82
+ NSString *iPadCoordString = options[kShareOptionIPadCoordinates];
83
+ NSArray *iPadCoordinates;
84
+
85
+ if (iPadCoordString != nil && iPadCoordString != [NSNull null]) {
86
+ iPadCoordinates = [iPadCoordString componentsSeparatedByString:@","];
87
+ } else {
88
+ iPadCoordinates = @[];
89
+ }
90
+
91
+
92
+ NSMutableArray *activityItems = [[NSMutableArray alloc] init];
93
+
94
+ if (message != (id)[NSNull null] && message != nil) {
95
+ [activityItems addObject:message];
96
+ }
97
+
98
+ if (filenames != (id)[NSNull null] && filenames != nil && filenames.count > 0) {
99
+ NSMutableArray *files = [[NSMutableArray alloc] init];
100
+ for (NSString* filename in filenames) {
101
+ NSObject *file = [self getImage:filename];
102
+ if (file == nil) {
103
+ file = [self getFile:filename];
104
+ }
105
+ if (file != nil) {
106
+ [files addObject:file];
107
+ }
108
+ }
109
+ [activityItems addObjectsFromArray:files];
110
+ }
111
+
112
+ if (urlString != (id)[NSNull null] && urlString != nil) {
113
+ [activityItems addObject:[NSURL URLWithString:[urlString SSURLEncodedString]]];
114
+ }
115
+
116
+ UIActivity *activity = [[UIActivity alloc] init];
117
+ NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil];
118
+ UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
119
+ if (subject != (id)[NSNull null] && subject != nil) {
120
+ [activityVC setValue:subject forKey:@"subject"];
121
+ }
122
+
123
+ if ([activityVC respondsToSelector:(@selector(setCompletionWithItemsHandler:))]) {
124
+ [activityVC setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray * returnedItems, NSError * activityError) {
125
+ if (completed == YES || activityType == nil) {
126
+ [self cleanupStoredFiles];
127
+ }
128
+ if (boolResponse) {
129
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed]
130
+ callbackId:command.callbackId];
131
+ } else {
132
+ NSDictionary * result = @{@"completed":@(completed), @"app":activityType == nil ? @"" : activityType};
133
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]
134
+ callbackId:command.callbackId];
135
+ }
136
+ }];
137
+ } else {
138
+ // let's suppress this warning otherwise folks will start opening issues while it's not relevant
139
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
140
+ [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) {
141
+ if (completed == YES || activityType == nil) {
142
+ [self cleanupStoredFiles];
143
+ }
144
+ NSDictionary * result = @{@"completed":@(completed), @"app":activityType == nil ? @"" : activityType};
145
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
146
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
147
+ }];
148
+ #pragma GCC diagnostic warning "-Wdeprecated-declarations"
149
+ }
150
+
151
+ NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"];
152
+ if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) {
153
+ activityVC.excludedActivityTypes = socialSharingExcludeActivities;
154
+ }
155
+
156
+ dispatch_async(dispatch_get_main_queue(), ^(void){
157
+ // iPad on iOS >= 8 needs a different approach
158
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
159
+ NSString* iPadCoords = [self getIPadPopupCoordinates];
160
+ if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) {
161
+ CGRect rect;
162
+ if ([iPadCoordinates count] == 4) {
163
+
164
+ rect = CGRectMake((int) [[iPadCoordinates objectAtIndex:0] integerValue], (int) [[iPadCoordinates objectAtIndex:1] integerValue], (int) [[iPadCoordinates objectAtIndex:2] integerValue], (int) [[iPadCoordinates objectAtIndex:3] integerValue]);
165
+ } else {
166
+ NSArray *comps = [iPadCoords componentsSeparatedByString:@","];
167
+ rect = [self getPopupRectFromIPadPopupCoordinates:comps];
168
+ }
169
+ if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
170
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
171
+ activityVC.popoverPresentationController.sourceView = self.webView;
172
+ activityVC.popoverPresentationController.sourceRect = rect;
173
+ #endif
174
+ } else {
175
+ _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC];
176
+ _popover.delegate = self;
177
+ [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
178
+ }
179
+ } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
180
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
181
+ activityVC.popoverPresentationController.sourceView = self.webView;
182
+ // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8)
183
+ CGRect rect;
184
+ if ([iPadCoordinates count] == 4) {
185
+ NSLog([[NSString alloc] initWithFormat:@"test %d", [[iPadCoordinates objectAtIndex:0] integerValue]]);
186
+ rect = CGRectMake((int) [[iPadCoordinates objectAtIndex:0] integerValue], (int) [[iPadCoordinates objectAtIndex:1] integerValue], (int) [[iPadCoordinates objectAtIndex:2] integerValue], (int) [[iPadCoordinates objectAtIndex:3] integerValue]);
187
+ } else {
188
+ NSArray *comps = [NSArray arrayWithObjects:
189
+ [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200],
190
+ [NSNumber numberWithInt:self.viewController.view.frame.size.height],
191
+ [NSNumber numberWithInt:400],
192
+ [NSNumber numberWithInt:400],
193
+ nil];
194
+ rect = [self getPopupRectFromIPadPopupCoordinates:comps];
195
+ }
196
+ activityVC.popoverPresentationController.sourceRect = rect;
197
+ #endif
198
+ }
199
+ }
200
+ [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil];
201
+ });
202
+ }
203
+
204
+ - (void)shareViaTwitter:(CDVInvokedUrlCommand*)command {
205
+ [self shareViaInternal:command type:SLServiceTypeTwitter];
206
+ }
207
+
208
+ - (void)shareViaFacebook:(CDVInvokedUrlCommand*)command {
209
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
210
+ }
211
+
212
+ - (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command {
213
+ // If Fb app is installed a message is not prefilled.
214
+ // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already.
215
+ NSString *message = [command.arguments objectAtIndex:0];
216
+ if (message != (id)[NSNull null]) {
217
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
218
+ BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9
219
+ if (fbAppInstalled) {
220
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
221
+ [pasteboard setValue:message forPasteboardType:@"public.utf8-plain-text"];
222
+ NSString *hint = [command.arguments objectAtIndex:4];
223
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
224
+ [alert show];
225
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
226
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
227
+ });
228
+ }
229
+ });
230
+ }
231
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
232
+ }
233
+
234
+ - (void)shareVia:(CDVInvokedUrlCommand*)command {
235
+ [self shareViaInternal:command type:[command.arguments objectAtIndex:4]];
236
+ }
237
+
238
+ - (void)canShareVia:(CDVInvokedUrlCommand*)command {
239
+ NSString *via = [command.arguments objectAtIndex:4];
240
+ CDVPluginResult * pluginResult;
241
+ if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) {
242
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
243
+ } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) {
244
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
245
+ } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) {
246
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
247
+ } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) {
248
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
249
+ } else if ([@"com.apple.social.facebook" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaFacebook]) {
250
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
251
+ } else if ([@"com.apple.social.twitter" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaTwitter]) {
252
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
253
+ } else if ([self isAvailableForSharing:command type:via]) {
254
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
255
+ } else {
256
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
257
+ }
258
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
259
+ }
260
+
261
+ - (void)canShareViaEmail:(CDVInvokedUrlCommand*)command {
262
+ if ([self isEmailAvailable]) {
263
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
264
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
265
+ } else {
266
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
267
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
268
+ }
269
+ }
270
+
271
+ - (bool)isEmailAvailable {
272
+ Class messageClass = (NSClassFromString(@"MFMailComposeViewController"));
273
+ return messageClass != nil && [messageClass canSendMail];
274
+ }
275
+
276
+ - (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command
277
+ type:(NSString *) type {
278
+
279
+ // isAvailableForServiceType returns true if you pass it a type that is not
280
+ // in the defined constants, this is probably a bug on apples part
281
+ if(!([type isEqualToString:SLServiceTypeFacebook]
282
+ || [type isEqualToString:SLServiceTypeTwitter]
283
+ || [type isEqualToString:SLServiceTypeTencentWeibo]
284
+ || [type isEqualToString:SLServiceTypeSinaWeibo])) {
285
+ return false;
286
+ }
287
+ // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed
288
+ @try {
289
+ return [SLComposeViewController isAvailableForServiceType:type];
290
+ }
291
+ @catch (NSException* exception) {
292
+ return false;
293
+ }
294
+ }
295
+
296
+ - (void)shareViaInternal:(CDVInvokedUrlCommand*)command
297
+ type:(NSString *) type {
298
+
299
+ NSString *message = [command.arguments objectAtIndex:0];
300
+ // subject is not supported by the SLComposeViewController
301
+ NSArray *filenames = [command.arguments objectAtIndex:2];
302
+ NSString *urlString = [command.arguments objectAtIndex:3];
303
+
304
+ // boldly invoke the target app, because the phone will display a nice message asking to configure the app
305
+ SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type];
306
+ if (message != (id)[NSNull null]) {
307
+ [composeViewController setInitialText:message];
308
+ }
309
+
310
+ for (NSString* filename in filenames) {
311
+ UIImage* image = [self getImage:filename];
312
+ if (image != nil) {
313
+ [composeViewController addImage:image];
314
+ }
315
+ }
316
+
317
+ if (urlString != (id)[NSNull null]) {
318
+ [composeViewController addURL:[NSURL URLWithString:[urlString SSURLEncodedString]]];
319
+ }
320
+
321
+ [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) {
322
+ if (SLComposeViewControllerResultCancelled == result) {
323
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"];
324
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
325
+ } else if (SLComposeViewControllerResultDone == result || [self isAvailableForSharing:command type:type]) {
326
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:true];
327
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
328
+ } else {
329
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
330
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
331
+ }
332
+ // required for iOS6 (issues #162 and #167)
333
+ [self.viewController dismissViewControllerAnimated:YES completion:nil];
334
+ }];
335
+ [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil];
336
+ }
337
+
338
+ - (void)shareViaEmail:(CDVInvokedUrlCommand*)command {
339
+ if ([self isEmailAvailable]) {
340
+
341
+ if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) {
342
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin"
343
+ message:@"Sharing via email is not supported on the iOS 8 simulator."
344
+ delegate:nil
345
+ cancelButtonTitle:@"OK"
346
+ otherButtonTitles:nil];
347
+ [alert show];
348
+ return;
349
+ }
350
+
351
+ [self cycleTheGlobalMailComposer];
352
+
353
+ self.globalMailComposer.mailComposeDelegate = self;
354
+
355
+ if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) {
356
+ NSString *message = [command.arguments objectAtIndex:0];
357
+ BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound;
358
+ [self.globalMailComposer setMessageBody:message isHTML:isHTML];
359
+ }
360
+
361
+ if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) {
362
+ [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]];
363
+ }
364
+
365
+ if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) {
366
+ [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]];
367
+ }
368
+
369
+ if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) {
370
+ [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]];
371
+ }
372
+
373
+ if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) {
374
+ [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]];
375
+ }
376
+
377
+ if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) {
378
+ NSArray* attachments = [command.arguments objectAtIndex:5];
379
+ NSFileManager* fileManager = [NSFileManager defaultManager];
380
+ for (NSString* path in attachments) {
381
+ NSURL *file = [self getFile:path];
382
+ NSData* data = [fileManager contentsAtPath:file.path];
383
+
384
+ if (!data) {
385
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"invalid attachment"];
386
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
387
+ return;
388
+ }
389
+
390
+ NSString* fileName;
391
+ NSString* mimeType;
392
+ NSString* basename = [self getBasenameFromAttachmentPath:path];
393
+
394
+ //Find data anywhere in string
395
+ NSRange rangeData = [basename rangeOfString:@"data:"];
396
+ if (rangeData.location == NSNotFound)
397
+ {
398
+ fileName = [basename pathComponents].lastObject;
399
+ mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]];
400
+ }
401
+ else
402
+ {
403
+ mimeType = (NSString*)[[[basename substringFromIndex:rangeData.location+rangeData.length] componentsSeparatedByString: @";"] objectAtIndex:0];
404
+
405
+ //Find df anywhere in string
406
+ NSRange rangeDF = [basename rangeOfString:@"df:"];
407
+ //If not found fallback to default name
408
+ if (rangeDF.location == NSNotFound) {
409
+ fileName = @"attachment.";
410
+ fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]];
411
+ } else {
412
+ //Found, apply name
413
+ fileName = (NSString*)[[[basename substringFromIndex:rangeDF.location+rangeDF.length] componentsSeparatedByString: @";"] objectAtIndex:0];
414
+ }
415
+
416
+
417
+ NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject];
418
+ data = [SocialSharing dataFromBase64String:base64content];
419
+ }
420
+ [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName];
421
+ }
422
+ }
423
+
424
+ // remember the command, because we need it in the didFinishWithResult method
425
+ _command = command;
426
+
427
+ dispatch_async(dispatch_get_main_queue(), ^{
428
+ [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil];
429
+ });
430
+ } else {
431
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
432
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
433
+ }
434
+ }
435
+
436
+ - (UIViewController*) getTopMostViewController {
437
+ UIViewController *presentingViewController = [[UIApplication sharedApplication] keyWindow].rootViewController;
438
+ while (presentingViewController.presentedViewController != nil) {
439
+ presentingViewController = presentingViewController.presentedViewController;
440
+ }
441
+ return presentingViewController;
442
+ }
443
+
444
+ - (NSString*) getBasenameFromAttachmentPath:(NSString*)path {
445
+ if ([path hasPrefix:@"base64:"]) {
446
+ NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""];
447
+ return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location];
448
+ }
449
+ return [path componentsSeparatedByString: @"?"][0];
450
+ }
451
+
452
+ - (NSString*) getMimeTypeFromFileExtension:(NSString*)extension {
453
+ if (!extension) {
454
+ return nil;
455
+ }
456
+ // Get the UTI from the file's extension
457
+ CFStringRef ext = (CFStringRef)CFBridgingRetain(extension);
458
+ CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL);
459
+ // Converting UTI to a mime type
460
+ NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType));
461
+ CFRelease(ext);
462
+ CFRelease(type);
463
+ if (result == nil) {
464
+ result = @"application/octet-stream";
465
+ }
466
+
467
+ return result;
468
+ }
469
+
470
+ /**
471
+ * Delegate will be called after the mail composer did finish an action
472
+ * to dismiss the view.
473
+ */
474
+ - (void) mailComposeController:(MFMailComposeViewController*)controller
475
+ didFinishWithResult:(MFMailComposeResult)result
476
+ error:(NSError*)error {
477
+ bool ok = result == MFMailComposeResultSent;
478
+ [self.globalMailComposer dismissViewControllerAnimated:YES completion:nil];
479
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
480
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
481
+ }
482
+
483
+ -(void)cycleTheGlobalMailComposer {
484
+ // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976
485
+ self.globalMailComposer = nil;
486
+ self.globalMailComposer = [[MFMailComposeViewController alloc] init];
487
+ }
488
+
489
+ - (bool)canShareViaSMS {
490
+ Class messageClass = (NSClassFromString(@"MFMessageComposeViewController"));
491
+ return messageClass != nil && [messageClass canSendText];
492
+ }
493
+
494
+ - (void)shareViaSMS:(CDVInvokedUrlCommand*)command {
495
+ if ([self canShareViaSMS]) {
496
+ NSDictionary* options = [command.arguments objectAtIndex:0];
497
+ NSString *phonenumbers = [command.arguments objectAtIndex:1];
498
+ NSString *message = [options objectForKey:@"message"];
499
+ NSString *subject = [options objectForKey:@"subject"];
500
+ NSString *image = [options objectForKey:@"image"];
501
+
502
+ MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
503
+ picker.messageComposeDelegate = (id) self;
504
+ if (message != (id)[NSNull null]) {
505
+ picker.body = message;
506
+ }
507
+ if (subject != (id)[NSNull null]) {
508
+ [picker setSubject:subject];
509
+ }
510
+ if (image != nil && image != (id)[NSNull null]) {
511
+ BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)];
512
+ if (canSendAttachments) {
513
+ NSURL *file = [self getFile:image];
514
+ if (file != nil) {
515
+ [picker addAttachmentURL:file withAlternateFilename:nil];
516
+ }
517
+ }
518
+ }
519
+
520
+ if (phonenumbers != (id)[NSNull null]) {
521
+ [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]];
522
+ }
523
+ // remember the command, because we need it in the didFinishWithResult method
524
+ _command = command;
525
+ dispatch_async(dispatch_get_main_queue(), ^{
526
+ picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
527
+ [[self getTopMostViewController] presentViewController:picker animated:NO completion:nil];
528
+ });
529
+ } else {
530
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
531
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
532
+ }
533
+ }
534
+
535
+ // Dismisses the SMS composition interface when users taps Cancel or Send
536
+ - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
537
+ bool ok = result == MessageComposeResultSent;
538
+ [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil];
539
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
540
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
541
+ }
542
+
543
+ - (bool)canShareViaInstagram {
544
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9
545
+ }
546
+
547
+ - (bool)canShareViaWhatsApp {
548
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9
549
+ }
550
+
551
+ - (bool)canShareViaFacebook {
552
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9
553
+ }
554
+
555
+ - (bool)canShareViaTwitter {
556
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"twitter://"]]; // requires whitelisting on iOS9
557
+ }
558
+
559
+ // this is only an internal test method for now, can be used to open a share sheet with 'Open in xx' links for tumblr, drive, dropbox, ..
560
+ - (void)openImage:(NSString *)imageName {
561
+ UIImage* image =[self getImage:imageName];
562
+ if (image != nil) {
563
+ NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"];
564
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
565
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
566
+ _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function
567
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
568
+ }
569
+ }
570
+
571
+ - (void)shareViaInstagram:(CDVInvokedUrlCommand*)command {
572
+
573
+ // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted.
574
+ // If it's not, this method will ask permission to the user on iOS9 for opening the app,
575
+ // which is of course better than Instagram sharing not working at all because you forgot to whitelist it.
576
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..)
577
+ if (!IsAtLeastiOSVersion(@"9.0")) {
578
+ if (![self canShareViaInstagram]) {
579
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
580
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
581
+ return;
582
+ }
583
+ }
584
+
585
+ NSString *message = [command.arguments objectAtIndex:0];
586
+ // subject is not supported by the SLComposeViewController
587
+ NSArray *filenames = [command.arguments objectAtIndex:2];
588
+
589
+ // only use the first image (for now.. maybe we can share in a loop?)
590
+ UIImage* image = nil;
591
+ for (NSString* filename in filenames) {
592
+ image = [self getImage:filename];
593
+ break;
594
+ }
595
+
596
+ // NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0];
597
+ NSString *tmpDir = NSTemporaryDirectory();
598
+ NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"];
599
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES];
600
+
601
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]];
602
+ _documentInteractionController.delegate = self;
603
+ _documentInteractionController.UTI = @"com.instagram.exclusivegram";
604
+
605
+ if (message != (id)[NSNull null]) {
606
+ // no longer working, so ..
607
+ _documentInteractionController.annotation = @{@"InstagramCaption" : message};
608
+
609
+ // .. we put the message on the clipboard (you app can prompt the user to paste it)
610
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
611
+ [pasteboard setValue:message forPasteboardType:@"public.utf8-plain-text"];
612
+ }
613
+
614
+ // remember the command for the delegate method
615
+ _command = command;
616
+
617
+ // test for #513
618
+ dispatch_async(dispatch_get_main_queue(), ^(void){
619
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES];
620
+ });
621
+ }
622
+
623
+ - (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command {
624
+
625
+ // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted.
626
+ // If it's not, this method will ask permission to the user on iOS9 for opening the app,
627
+ // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it.
628
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..)
629
+ if (!IsAtLeastiOSVersion(@"9.0")) {
630
+ if (![self canShareViaWhatsApp]) {
631
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
632
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
633
+ return;
634
+ }
635
+ }
636
+
637
+ NSString *message = [command.arguments objectAtIndex:0];
638
+ // subject is not supported by the SLComposeViewController
639
+ NSArray *filenames = [command.arguments objectAtIndex:2];
640
+ NSString *urlString = [command.arguments objectAtIndex:3];
641
+ NSString *abid = [command.arguments objectAtIndex:4];
642
+ NSString *phone = [command.arguments objectAtIndex:5];
643
+
644
+ // only use the first image (for now.. maybe we can share in a loop?)
645
+ UIImage *image = nil;
646
+ for (NSString *filename in filenames) {
647
+ image = [self getImage:filename];
648
+ break;
649
+ }
650
+
651
+ // with WhatsApp, we can share an image OR text+url.. image wins if set
652
+ if (image != nil) {
653
+ NSString *savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.jpg"];
654
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
655
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
656
+ _documentInteractionController.UTI = @"net.whatsapp.image";
657
+ _documentInteractionController.delegate = self;
658
+ _command = command;
659
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
660
+ } else {
661
+ // append an url to a message, if both are passed
662
+ NSString *shareString = @"";
663
+ if (message != (id)[NSNull null]) {
664
+ shareString = message;
665
+ }
666
+ if (urlString != (id)[NSNull null]) {
667
+ if ([shareString isEqual: @""]) {
668
+ shareString = urlString;
669
+ } else {
670
+ shareString = [NSString stringWithFormat:@"%@ %@", shareString, [urlString SSURLEncodedString]];
671
+ }
672
+ }
673
+ NSString *encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
674
+ // also encode the '=' character
675
+ encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"];
676
+ encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
677
+ NSString *abidString = @"";
678
+ if (abid != (id)[NSNull null]) {
679
+ abidString = [NSString stringWithFormat:@"abid=%@&", abid];
680
+ }
681
+ NSString *phoneString = @"";
682
+ if (phone != (id)[NSNull null]) {
683
+ phoneString = [NSString stringWithFormat:@"phone=%@&", phone];
684
+ }
685
+ NSString *encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?%@%@text=%@", abidString, phoneString, encodedShareString];
686
+
687
+ NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp];
688
+ [[UIApplication sharedApplication] openURL: whatsappURL];
689
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
690
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
691
+ }
692
+ }
693
+
694
+ - (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command {
695
+ self.command = command;
696
+ NSArray *filenames = [command.arguments objectAtIndex:0];
697
+ [self.commandDelegate runInBackground:^{
698
+ bool shared = false;
699
+ for (NSString* filename in filenames) {
700
+ UIImage* image = [self getImage:filename];
701
+ if (image != nil) {
702
+ shared = true;
703
+ UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil);
704
+ }
705
+ }
706
+ if (!shared) {
707
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"];
708
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
709
+ }
710
+ }];
711
+ }
712
+
713
+ // called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback)
714
+ // but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem
715
+ - (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo {
716
+ if (error) {
717
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription];
718
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
719
+ } else {
720
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
721
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
722
+ }
723
+ }
724
+
725
+ -(UIImage*)getImage: (NSString *)imageName {
726
+ UIImage *image = nil;
727
+ if (imageName != (id)[NSNull null]) {
728
+ if ([imageName hasPrefix:@"http"]) {
729
+ image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]];
730
+ } else if ([imageName hasPrefix:@"www/"]) {
731
+ image = [UIImage imageNamed:imageName];
732
+ } else if ([imageName hasPrefix:@"file://"]) {
733
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]];
734
+ } else if ([imageName hasPrefix:@"data:"]) {
735
+ // using a base64 encoded string
736
+ NSURL *imageURL = [NSURL URLWithString:imageName];
737
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
738
+ image = [UIImage imageWithData:imageData];
739
+ } else if ([imageName hasPrefix:@"assets-library://"]) {
740
+ // use assets-library
741
+ NSURL *imageURL = [NSURL URLWithString:imageName];
742
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
743
+ image = [UIImage imageWithData:imageData];
744
+ } else {
745
+ // assume anywhere else, on the local filesystem
746
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]];
747
+ }
748
+ }
749
+ return image;
750
+ }
751
+
752
+ -(NSURL*)getFile: (NSString *)fileName {
753
+ NSURL *file = nil;
754
+ if (fileName != (id)[NSNull null]) {
755
+ NSRange rangeData = [fileName rangeOfString:@"data:"];
756
+ if ([fileName hasPrefix:@"http"]) {
757
+ NSURL *url = [NSURL URLWithString:fileName];
758
+ NSData *fileData = [NSData dataWithContentsOfURL:url];
759
+ NSURLRequest *request = [NSURLRequest requestWithURL: url];
760
+ NSHTTPURLResponse *response;
761
+ [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: nil];
762
+ if ([response respondsToSelector:@selector(allHeaderFields)]) {
763
+ NSDictionary *dictionary = [response allHeaderFields];
764
+ NSLog([dictionary description]);
765
+ NSString *name = dictionary[@"Content-Disposition"];
766
+ if (name == nil){
767
+ NSString *name = (NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject];
768
+ file = [NSURL fileURLWithPath:[self storeInFile:[name componentsSeparatedByString: @"?"][0] fileData:fileData]];
769
+ } else {
770
+ file = [NSURL fileURLWithPath:[self storeInFile:[[name componentsSeparatedByString:@"="] lastObject] fileData:fileData]];
771
+ }
772
+ } else {
773
+ NSString *name = (NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject];
774
+ file = [NSURL fileURLWithPath:[self storeInFile:[name componentsSeparatedByString: @"?"][0] fileData:fileData]];
775
+ }
776
+ } else if ([fileName hasPrefix:@"www/"]) {
777
+ NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
778
+ NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName];
779
+ file = [NSURL fileURLWithPath:fullPath];
780
+ } else if ([fileName hasPrefix:@"file://"]) {
781
+ // stripping the first 6 chars, because the path should start with / instead of file://
782
+ file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]];
783
+ } else if (rangeData.location != NSNotFound ){
784
+ //If found "data:"
785
+ NSString *fileType = (NSString*)[[[fileName substringFromIndex:rangeData.location+rangeData.length] componentsSeparatedByString: @";"] objectAtIndex:0];
786
+
787
+ NSString* attachmentName;
788
+ //Find df anywhere in string
789
+ NSRange rangeDF = [fileName rangeOfString:@"df:"];
790
+ //If not found fallback to default name
791
+ if (rangeDF.location == NSNotFound) {
792
+ attachmentName = @"attachment.";
793
+ attachmentName = [attachmentName stringByAppendingString:(NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject]];
794
+ } else {
795
+ //Found, apply name
796
+ attachmentName = (NSString*)[[[fileName substringFromIndex:rangeDF.location+rangeDF.length] componentsSeparatedByString: @";"] objectAtIndex:0];
797
+ }
798
+
799
+
800
+ NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject];
801
+ NSData* data = [SocialSharing dataFromBase64String:base64content];
802
+ file = [NSURL fileURLWithPath:[self storeInFile:attachmentName fileData:data]];
803
+
804
+ } else {
805
+ // assume anywhere else, on the local filesystem
806
+ file = [NSURL fileURLWithPath:fileName];
807
+ }
808
+ }
809
+ return file;
810
+ }
811
+
812
+ -(NSString*) storeInFile: (NSString*) fileName
813
+ fileData: (NSData*) fileData {
814
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
815
+ NSString *documentsDirectory = [paths objectAtIndex:0];
816
+ // remove filename wrapping quotes
817
+ NSString *filenameWithoutQuote = [fileName stringByReplacingOccurrencesOfString:@"'" withString:@""];
818
+ NSString *filePath = [documentsDirectory stringByAppendingPathComponent:filenameWithoutQuote];
819
+ [fileData writeToFile:filePath atomically:YES];
820
+ _tempStoredFile = filePath;
821
+ return filePath;
822
+ }
823
+
824
+ - (void) cleanupStoredFiles {
825
+ if (_tempStoredFile != nil) {
826
+ NSError *error;
827
+ [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error];
828
+ }
829
+ }
830
+
831
+ + (NSData*) dataFromBase64String:(NSString*)aString {
832
+ return [[NSData alloc] initWithBase64EncodedString:aString options:0];
833
+ }
834
+
835
+ #pragma mark - UIPopoverControllerDelegate methods
836
+
837
+ - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view {
838
+ NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","];
839
+ CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps];
840
+ rect->origin = newRect.origin;
841
+ }
842
+
843
+ - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
844
+ _popover = nil;
845
+ }
846
+
847
+ #pragma mark - UIDocumentInteractionControllerDelegate methods
848
+
849
+ - (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application {
850
+ // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only)
851
+ NSLog(@"SocialSharing app selected: %@", application);
852
+ }
853
+
854
+ - (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller {
855
+ if (self.command != nil) {
856
+ CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
857
+ [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId];
858
+ }
859
+ }
860
+
861
+ @end