diff options
Diffstat (limited to 'StoneIsland/platforms/ios/Stone Island/Plugins')
31 files changed, 5894 insertions, 0 deletions
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/README b/StoneIsland/platforms/ios/Stone Island/Plugins/README new file mode 100644 index 00000000..87df09f2 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/README @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder. diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h new file mode 100644 index 00000000..7cfb3063 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVPlugin.h> + +@interface CDVLogger : CDVPlugin + +- (void)logLevel:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m new file mode 100644 index 00000000..ccfa3a51 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m @@ -0,0 +1,38 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVLogger.h" +#import <Cordova/CDV.h> + +@implementation CDVLogger + +/* log a message */ +- (void)logLevel:(CDVInvokedUrlCommand*)command +{ + id level = [command argumentAtIndex:0]; + id message = [command argumentAtIndex:1]; + + if ([level isEqualToString:@"LOG"]) { + NSLog(@"%@", message); + } else { + NSLog(@"%@: %@", level, message); + } +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h new file mode 100644 index 00000000..a146d882 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h @@ -0,0 +1,30 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <UIKit/UIKit.h> +#import <Cordova/CDVPlugin.h> + +@interface CDVDevice : CDVPlugin +{} + ++ (NSString*)cordovaVersion; + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m new file mode 100644 index 00000000..4d75a574 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m @@ -0,0 +1,112 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> +#include "TargetConditionals.h" + +#import <Cordova/CDV.h> +#import "CDVDevice.h" + +@implementation UIDevice (ModelVersion) + +- (NSString*)modelVersion +{ + size_t size; + + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char* machine = malloc(size); + sysctlbyname("hw.machine", machine, &size, NULL, 0); + NSString* platform = [NSString stringWithUTF8String:machine]; + free(machine); + + return platform; +} + +@end + +@interface CDVDevice () {} +@end + +@implementation CDVDevice + +- (NSString*)uniqueAppInstanceIdentifier:(UIDevice*)device +{ + NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + static NSString* UUID_KEY = @"CDVUUID"; + + // Check user defaults first to maintain backwards compaitibility with previous versions + // which didn't user identifierForVendor + NSString* app_uuid = [userDefaults stringForKey:UUID_KEY]; + if (app_uuid == nil) { + if ([device respondsToSelector:@selector(identifierForVendor)]) { + app_uuid = [[device identifierForVendor] UUIDString]; + } else { + CFUUIDRef uuid = CFUUIDCreate(NULL); + app_uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + } + + [userDefaults setObject:app_uuid forKey:UUID_KEY]; + [userDefaults synchronize]; + } + + return app_uuid; +} + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command +{ + NSDictionary* deviceProperties = [self deviceProperties]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSDictionary*)deviceProperties +{ + UIDevice* device = [UIDevice currentDevice]; + + return @{ + @"manufacturer": @"Apple", + @"model": [device modelVersion], + @"platform": @"iOS", + @"version": [device systemVersion], + @"uuid": [self uniqueAppInstanceIdentifier:device], + @"cordova": [[self class] cordovaVersion], + @"isVirtual": @([self isVirtual]) + }; +} + ++ (NSString*)cordovaVersion +{ + return CDV_VERSION; +} + +- (BOOL)isVirtual +{ + #if TARGET_OS_SIMULATOR + return true; + #elif TARGET_IPHONE_SIMULATOR + return true; + #else + return false; + #endif +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h new file mode 100644 index 00000000..9253f6a9 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h @@ -0,0 +1,37 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> +#import <AudioToolbox/AudioServices.h> +#import <Cordova/CDVPlugin.h> + +@interface CDVNotification : CDVPlugin <UIAlertViewDelegate>{} + +- (void)alert:(CDVInvokedUrlCommand*)command; +- (void)confirm:(CDVInvokedUrlCommand*)command; +- (void)prompt:(CDVInvokedUrlCommand*)command; +- (void)beep:(CDVInvokedUrlCommand*)command; + +@end + +@interface CDVAlertView : UIAlertView {} +@property (nonatomic, copy) NSString* callbackId; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m new file mode 100644 index 00000000..0dd3d2cb --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m @@ -0,0 +1,256 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVNotification.h" + +#define DIALOG_TYPE_ALERT @"alert" +#define DIALOG_TYPE_PROMPT @"prompt" + +static void soundCompletionCallback(SystemSoundID ssid, void* data); +static NSMutableArray *alertList = nil; + +@implementation CDVNotification + +/* + * showDialogWithMessage - Common method to instantiate the alert view for alert, confirm, and prompt notifications. + * Parameters: + * message The alert view message. + * title The alert view title. + * buttons The array of customized strings for the buttons. + * defaultText The input text for the textbox (if textbox exists). + * callbackId The commmand callback id. + * dialogType The type of alert view [alert | prompt]. + */ +- (void)showDialogWithMessage:(NSString*)message title:(NSString*)title buttons:(NSArray*)buttons defaultText:(NSString*)defaultText callbackId:(NSString*)callbackId dialogType:(NSString*)dialogType +{ + + int count = (int)[buttons count]; +#ifdef __IPHONE_8_0 + if (NSClassFromString(@"UIAlertController")) { + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + + if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.3) { + + CGRect alertFrame = [UIScreen mainScreen].applicationFrame; + + if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { + // swap the values for the app frame since it is now in landscape + CGFloat temp = alertFrame.size.width; + alertFrame.size.width = alertFrame.size.height; + alertFrame.size.height = temp; + } + + alertController.view.frame = alertFrame; + } + + __weak CDVNotification* weakNotif = self; + + for (int n = 0; n < count; n++) { + [alertController addAction:[UIAlertAction actionWithTitle:[buttons objectAtIndex:n] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) + { + CDVPluginResult* result; + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) + { + NSString* value0 = [[alertController.textFields objectAtIndex:0] text]; + NSDictionary* info = @{ + @"buttonIndex":@(n + 1), + @"input1":(value0 ? value0 : [NSNull null]) + }; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; + } + else + { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(n + 1)]; + } + + [weakNotif.commandDelegate sendPluginResult:result callbackId:callbackId]; + }]]; + } + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) { + + [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.text = defaultText; + }]; + } + + if(!alertList) + alertList = [[NSMutableArray alloc] init]; + [alertList addObject:alertController]; + + if ([alertList count]==1) { + [self presentAlertcontroller]; + } + + } + else + { +#endif + + CDVAlertView* alertView = [[CDVAlertView alloc] + initWithTitle:title + message:message + delegate:self + cancelButtonTitle:nil + otherButtonTitles:nil]; + + alertView.callbackId = callbackId; + + + + for (int n = 0; n < count; n++) { + [alertView addButtonWithTitle:[buttons objectAtIndex:n]]; + } + + if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) { + alertView.alertViewStyle = UIAlertViewStylePlainTextInput; + UITextField* textField = [alertView textFieldAtIndex:0]; + textField.text = defaultText; + } + + [alertView show]; +#ifdef __IPHONE_8_0 + } +#endif + +} + +- (void)alert:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSString* buttons = [command argumentAtIndex:2]; + + [self showDialogWithMessage:message title:title buttons:@[buttons] defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT]; +} + +- (void)confirm:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSArray* buttons = [command argumentAtIndex:2]; + + [self showDialogWithMessage:message title:title buttons:buttons defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT]; +} + +- (void)prompt:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* message = [command argumentAtIndex:0]; + NSString* title = [command argumentAtIndex:1]; + NSArray* buttons = [command argumentAtIndex:2]; + NSString* defaultText = [command argumentAtIndex:3]; + + [self showDialogWithMessage:message title:title buttons:buttons defaultText:defaultText callbackId:callbackId dialogType:DIALOG_TYPE_PROMPT]; +} + +/** + * Callback invoked when an alert dialog's buttons are clicked. + */ +- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + CDVAlertView* cdvAlertView = (CDVAlertView*)alertView; + CDVPluginResult* result; + + // Determine what gets returned to JS based on the alert view type. + if (alertView.alertViewStyle == UIAlertViewStyleDefault) { + // For alert and confirm, return button index as int back to JS. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(buttonIndex + 1)]; + } else { + // For prompt, return button index and input text back to JS. + NSString* value0 = [[alertView textFieldAtIndex:0] text]; + NSDictionary* info = @{ + @"buttonIndex":@(buttonIndex + 1), + @"input1":(value0 ? value0 : [NSNull null]) + }; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; + } + [self.commandDelegate sendPluginResult:result callbackId:cdvAlertView.callbackId]; +} + +- (void)didPresentAlertView:(UIAlertView*)alertView +{ + //show keyboard on iOS 8 + if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput){ + [[alertView textFieldAtIndex:0] selectAll:nil]; + } +} + +static void playBeep(int count) { + SystemSoundID completeSound; + NSInteger cbDataCount = count; + NSURL* audioPath = [[NSBundle mainBundle] URLForResource:@"CDVNotification.bundle/beep" withExtension:@"wav"]; + #if __has_feature(objc_arc) + AudioServicesCreateSystemSoundID((__bridge CFURLRef)audioPath, &completeSound); + #else + AudioServicesCreateSystemSoundID((CFURLRef)audioPath, &completeSound); + #endif + AudioServicesAddSystemSoundCompletion(completeSound, NULL, NULL, soundCompletionCallback, (void*)(cbDataCount-1)); + AudioServicesPlaySystemSound(completeSound); +} + +static void soundCompletionCallback(SystemSoundID ssid, void* data) { + int count = (int)data; + AudioServicesRemoveSystemSoundCompletion (ssid); + AudioServicesDisposeSystemSoundID(ssid); + if (count > 0) { + playBeep(count); + } +} + +- (void)beep:(CDVInvokedUrlCommand*)command +{ + NSNumber* count = [command argumentAtIndex:0 withDefault:[NSNumber numberWithInt:1]]; + playBeep([count intValue]); +} + +-(UIViewController *)getTopPresentedViewController { + UIViewController *presentingViewController = self.viewController; + while(presentingViewController.presentedViewController != nil && ![presentingViewController.presentedViewController isBeingDismissed]) + { + presentingViewController = presentingViewController.presentedViewController; + } + return presentingViewController; +} + +-(void)presentAlertcontroller { + + __weak CDVNotification* weakNotif = self; + [self.getTopPresentedViewController presentViewController:[alertList firstObject] animated:YES completion:^{ + [alertList removeObject:[alertList firstObject]]; + if ([alertList count]>0) { + [weakNotif presentAlertcontroller]; + } + }]; + +} + +@end + +@implementation CDVAlertView + +@synthesize callbackId; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h new file mode 100644 index 00000000..cce2738f --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h @@ -0,0 +1,70 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <UIKit/UIKit.h> +#import <CoreLocation/CoreLocation.h> +#import <Cordova/CDVPlugin.h> + +enum CDVLocationStatus { + PERMISSIONDENIED = 1, + POSITIONUNAVAILABLE, + TIMEOUT +}; +typedef NSUInteger CDVLocationStatus; + +// simple object to keep track of location information +@interface CDVLocationData : NSObject { + CDVLocationStatus locationStatus; + NSMutableArray* locationCallbacks; + NSMutableDictionary* watchCallbacks; + CLLocation* locationInfo; +} + +@property (nonatomic, assign) CDVLocationStatus locationStatus; +@property (nonatomic, strong) CLLocation* locationInfo; +@property (nonatomic, strong) NSMutableArray* locationCallbacks; +@property (nonatomic, strong) NSMutableDictionary* watchCallbacks; + +@end + +@interface CDVLocation : CDVPlugin <CLLocationManagerDelegate>{ + @private BOOL __locationStarted; + @private BOOL __highAccuracyEnabled; + CDVLocationData* locationData; +} + +@property (nonatomic, strong) CLLocationManager* locationManager; +@property (nonatomic, strong) CDVLocationData* locationData; + +- (void)getLocation:(CDVInvokedUrlCommand*)command; +- (void)addWatch:(CDVInvokedUrlCommand*)command; +- (void)clearWatch:(CDVInvokedUrlCommand*)command; +- (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallback; +- (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message; +- (void)startLocation:(BOOL)enableHighAccuracy; + +- (void)locationManager:(CLLocationManager*)manager + didUpdateToLocation:(CLLocation*)newLocation + fromLocation:(CLLocation*)oldLocation; + +- (void)locationManager:(CLLocationManager*)manager + didFailWithError:(NSError*)error; + +- (BOOL)isLocationServicesEnabled; +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m new file mode 100644 index 00000000..e4b34a11 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m @@ -0,0 +1,367 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVLocation.h" + +#pragma mark Constants + +#define kPGLocationErrorDomain @"kPGLocationErrorDomain" +#define kPGLocationDesiredAccuracyKey @"desiredAccuracy" +#define kPGLocationForcePromptKey @"forcePrompt" +#define kPGLocationDistanceFilterKey @"distanceFilter" +#define kPGLocationFrequencyKey @"frequency" + +#pragma mark - +#pragma mark Categories + +@implementation CDVLocationData + +@synthesize locationStatus, locationInfo, locationCallbacks, watchCallbacks; +- (CDVLocationData*)init +{ + self = (CDVLocationData*)[super init]; + if (self) { + self.locationInfo = nil; + self.locationCallbacks = nil; + self.watchCallbacks = nil; + } + return self; +} + +@end + +#pragma mark - +#pragma mark CDVLocation + +@implementation CDVLocation + +@synthesize locationManager, locationData; + +- (void)pluginInitialize +{ + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; // Tells the location manager to send updates to this object + __locationStarted = NO; + __highAccuracyEnabled = NO; + self.locationData = nil; +} + +- (BOOL)isAuthorized +{ + BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + + if (authorizationStatusClassPropertyAvailable) { + NSUInteger authStatus = [CLLocationManager authorizationStatus]; +#ifdef __IPHONE_8_0 + if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { //iOS 8.0+ + return (authStatus == kCLAuthorizationStatusAuthorizedWhenInUse) || (authStatus == kCLAuthorizationStatusAuthorizedAlways) || (authStatus == kCLAuthorizationStatusNotDetermined); + } +#endif + return (authStatus == kCLAuthorizationStatusAuthorized) || (authStatus == kCLAuthorizationStatusNotDetermined); + } + + // by default, assume YES (for iOS < 4.2) + return YES; +} + +- (BOOL)isLocationServicesEnabled +{ + BOOL locationServicesEnabledInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 3.x + BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x + + if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x + return [CLLocationManager locationServicesEnabled]; + } else if (locationServicesEnabledInstancePropertyAvailable) { // iOS 2.x, iOS 3.x + return [(id)self.locationManager locationServicesEnabled]; + } else { + return NO; + } +} + +- (void)startLocation:(BOOL)enableHighAccuracy +{ + if (![self isLocationServicesEnabled]) { + [self returnLocationError:PERMISSIONDENIED withMessage:@"Location services are not enabled."]; + return; + } + if (![self isAuthorized]) { + NSString* message = nil; + BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + if (authStatusAvailable) { + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined) { + // could return POSITION_UNAVAILABLE but need to coordinate with other platforms + message = @"User undecided on application's use of location services."; + } else if (code == kCLAuthorizationStatusRestricted) { + message = @"Application's use of location services is restricted."; + } + } + // PERMISSIONDENIED is only PositionError that makes sense when authorization denied + [self returnLocationError:PERMISSIONDENIED withMessage:message]; + + return; + } + +#ifdef __IPHONE_8_0 + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) { //iOS8+ + __highAccuracyEnabled = enableHighAccuracy; + if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]){ + [self.locationManager requestWhenInUseAuthorization]; + } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) { + [self.locationManager requestAlwaysAuthorization]; + } else { + NSLog(@"[Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file."); + } + return; + } +#endif + + // Tell the location manager to start notifying us of location updates. We + // first stop, and then start the updating to ensure we get at least one + // update, even if our location did not change. + [self.locationManager stopUpdatingLocation]; + [self.locationManager startUpdatingLocation]; + __locationStarted = YES; + if (enableHighAccuracy) { + __highAccuracyEnabled = YES; + // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a + // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery. + self.locationManager.distanceFilter = 5; + // Set desired accuracy to Best. + self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; + } else { + __highAccuracyEnabled = NO; + self.locationManager.distanceFilter = 10; + self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; + } +} + +- (void)_stopLocation +{ + if (__locationStarted) { + if (![self isLocationServicesEnabled]) { + return; + } + + [self.locationManager stopUpdatingLocation]; + __locationStarted = NO; + __highAccuracyEnabled = NO; + } +} + +- (void)locationManager:(CLLocationManager*)manager + didUpdateToLocation:(CLLocation*)newLocation + fromLocation:(CLLocation*)oldLocation +{ + CDVLocationData* cData = self.locationData; + + cData.locationInfo = newLocation; + if (self.locationData.locationCallbacks.count > 0) { + for (NSString* callbackId in self.locationData.locationCallbacks) { + [self returnLocationInfo:callbackId andKeepCallback:NO]; + } + + [self.locationData.locationCallbacks removeAllObjects]; + } + if (self.locationData.watchCallbacks.count > 0) { + for (NSString* timerId in self.locationData.watchCallbacks) { + [self returnLocationInfo:[self.locationData.watchCallbacks objectForKey:timerId] andKeepCallback:YES]; + } + } else { + // No callbacks waiting on us anymore, turn off listening. + [self _stopLocation]; + } +} + +- (void)getLocation:(CDVInvokedUrlCommand*)command +{ + [self.commandDelegate runInBackground:^{ + NSString* callbackId = command.callbackId; + BOOL enableHighAccuracy = [[command argumentAtIndex:0] boolValue]; + + if ([self isLocationServicesEnabled] == NO) { + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; + [posError setObject:@"Location services are disabled." forKey:@"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!self.locationData) { + self.locationData = [[CDVLocationData alloc] init]; + } + CDVLocationData* lData = self.locationData; + if (!lData.locationCallbacks) { + lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1]; + } + + if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) { + // add the callbackId into the array so we can call back when get data + if (callbackId != nil) { + [lData.locationCallbacks addObject:callbackId]; + } + // Tell the location manager to start notifying us of heading updates + [self startLocation:enableHighAccuracy]; + } else { + [self returnLocationInfo:callbackId andKeepCallback:NO]; + } + } + }]; +} + +- (void)addWatch:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* timerId = [command argumentAtIndex:0]; + BOOL enableHighAccuracy = [[command argumentAtIndex:1] boolValue]; + + if (!self.locationData) { + self.locationData = [[CDVLocationData alloc] init]; + } + CDVLocationData* lData = self.locationData; + + if (!lData.watchCallbacks) { + lData.watchCallbacks = [NSMutableDictionary dictionaryWithCapacity:1]; + } + + // add the callbackId into the dictionary so we can call back whenever get data + [lData.watchCallbacks setObject:callbackId forKey:timerId]; + + if ([self isLocationServicesEnabled] == NO) { + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; + [posError setObject:@"Location services are disabled." forKey:@"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) { + // Tell the location manager to start notifying us of location updates + [self startLocation:enableHighAccuracy]; + } + } +} + +- (void)clearWatch:(CDVInvokedUrlCommand*)command +{ + NSString* timerId = [command argumentAtIndex:0]; + + if (self.locationData && self.locationData.watchCallbacks && [self.locationData.watchCallbacks objectForKey:timerId]) { + [self.locationData.watchCallbacks removeObjectForKey:timerId]; + if([self.locationData.watchCallbacks count] == 0) { + [self _stopLocation]; + } + } +} + +- (void)stopLocation:(CDVInvokedUrlCommand*)command +{ + [self _stopLocation]; +} + +- (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallback +{ + CDVPluginResult* result = nil; + CDVLocationData* lData = self.locationData; + + if (lData && !lData.locationInfo) { + // return error + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:POSITIONUNAVAILABLE]; + } else if (lData && lData.locationInfo) { + CLLocation* lInfo = lData.locationInfo; + NSMutableDictionary* returnInfo = [NSMutableDictionary dictionaryWithCapacity:8]; + NSNumber* timestamp = [NSNumber numberWithDouble:([lInfo.timestamp timeIntervalSince1970] * 1000)]; + [returnInfo setObject:timestamp forKey:@"timestamp"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.speed] forKey:@"velocity"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.verticalAccuracy] forKey:@"altitudeAccuracy"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.horizontalAccuracy] forKey:@"accuracy"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.course] forKey:@"heading"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.altitude] forKey:@"altitude"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.latitude] forKey:@"latitude"]; + [returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.longitude] forKey:@"longitude"]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo]; + [result setKeepCallbackAsBool:keepCallback]; + } + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message +{ + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + + [posError setObject:[NSNumber numberWithUnsignedInteger:errorCode] forKey:@"code"]; + [posError setObject:message ? message:@"" forKey:@"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + + for (NSString* callbackId in self.locationData.locationCallbacks) { + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + + [self.locationData.locationCallbacks removeAllObjects]; + + for (NSString* callbackId in self.locationData.watchCallbacks) { + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error +{ + NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]); + + CDVLocationData* lData = self.locationData; + if (lData && __locationStarted) { + // TODO: probably have to once over the various error codes and return one of: + // PositionError.PERMISSION_DENIED = 1; + // PositionError.POSITION_UNAVAILABLE = 2; + // PositionError.TIMEOUT = 3; + NSUInteger positionError = POSITIONUNAVAILABLE; + if (error.code == kCLErrorDenied) { + positionError = PERMISSIONDENIED; + } + [self returnLocationError:positionError withMessage:[error localizedDescription]]; + } + + if (error.code != kCLErrorLocationUnknown) { + [self.locationManager stopUpdatingLocation]; + __locationStarted = NO; + } +} + +//iOS8+ +-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status +{ + if(!__locationStarted){ + [self startLocation:__highAccuracyEnabled]; + } +} + +- (void)dealloc +{ + self.locationManager.delegate = nil; +} + +- (void)onReset +{ + [self _stopLocation]; + [self.locationManager stopUpdatingHeading]; +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h new file mode 100644 index 00000000..6bb0ec16 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h @@ -0,0 +1,112 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVPlugin.h> +#import <Cordova/CDVInvokedUrlCommand.h> +#import <Cordova/CDVScreenOrientationDelegate.h> + +#ifdef __CORDOVA_4_0_0 + #import <Cordova/CDVUIWebViewDelegate.h> +#else + #import <Cordova/CDVWebViewDelegate.h> +#endif + +@class CDVInAppBrowserViewController; + +@interface CDVInAppBrowser : CDVPlugin { +} + +@property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController; +@property (nonatomic, copy) NSString* callbackId; +@property (nonatomic, copy) NSRegularExpression *callbackIdPattern; + +- (void)open:(CDVInvokedUrlCommand*)command; +- (void)close:(CDVInvokedUrlCommand*)command; +- (void)injectScriptCode:(CDVInvokedUrlCommand*)command; +- (void)show:(CDVInvokedUrlCommand*)command; + +@end + +@interface CDVInAppBrowserOptions : NSObject {} + +@property (nonatomic, assign) BOOL location; +@property (nonatomic, assign) BOOL toolbar; +@property (nonatomic, copy) NSString* closebuttoncaption; +@property (nonatomic, copy) NSString* toolbarposition; +@property (nonatomic, assign) BOOL clearcache; +@property (nonatomic, assign) BOOL clearsessioncache; + +@property (nonatomic, copy) NSString* presentationstyle; +@property (nonatomic, copy) NSString* transitionstyle; + +@property (nonatomic, assign) BOOL enableviewportscale; +@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction; +@property (nonatomic, assign) BOOL allowinlinemediaplayback; +@property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction; +@property (nonatomic, assign) BOOL suppressesincrementalrendering; +@property (nonatomic, assign) BOOL hidden; +@property (nonatomic, assign) BOOL disallowoverscroll; + ++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options; + +@end + +@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{ + @private + NSString* _userAgent; + NSString* _prevUserAgent; + NSInteger _userAgentLockToken; + CDVInAppBrowserOptions *_browserOptions; + +#ifdef __CORDOVA_4_0_0 + CDVUIWebViewDelegate* _webViewDelegate; +#else + CDVWebViewDelegate* _webViewDelegate; +#endif + +} + +@property (nonatomic, strong) IBOutlet UIWebView* webView; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton; +@property (nonatomic, strong) IBOutlet UILabel* addressLabel; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton; +@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner; +@property (nonatomic, strong) IBOutlet UIToolbar* toolbar; + +@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; +@property (nonatomic, weak) CDVInAppBrowser* navigationDelegate; +@property (nonatomic) NSURL* currentURL; + +- (void)close; +- (void)navigateTo:(NSURL*)url; +- (void)showLocationBar:(BOOL)show; +- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition; +- (void)setCloseButtonTitle:(NSString*)title; + +- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions; + +@end + +@interface CDVInAppBrowserNavigationController : UINavigationController + +@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; + +@end + diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m new file mode 100644 index 00000000..b342ca73 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m @@ -0,0 +1,1075 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInAppBrowser.h" +#import <Cordova/CDVPluginResult.h> +#import <Cordova/CDVUserAgentUtil.h> + +#define kInAppBrowserTargetSelf @"_self" +#define kInAppBrowserTargetSystem @"_system" +#define kInAppBrowserTargetBlank @"_blank" + +#define kInAppBrowserToolbarBarPositionBottom @"bottom" +#define kInAppBrowserToolbarBarPositionTop @"top" + +#define TOOLBAR_HEIGHT 44.0 +#define LOCATIONBAR_HEIGHT 21.0 +#define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT)) + +#pragma mark CDVInAppBrowser + +@interface CDVInAppBrowser () { + NSInteger _previousStatusBarStyle; +} +@end + +@implementation CDVInAppBrowser + +- (void)pluginInitialize +{ + _previousStatusBarStyle = -1; + _callbackIdPattern = nil; +} + +- (id)settingForKey:(NSString*)key +{ + return [self.commandDelegate.settings objectForKey:[key lowercaseString]]; +} + +- (void)onReset +{ + [self close:nil]; +} + +- (void)close:(CDVInvokedUrlCommand*)command +{ + if (self.inAppBrowserViewController == nil) { + NSLog(@"IAB.close() called but it was already closed."); + return; + } + // Things are cleaned up in browserExit. + [self.inAppBrowserViewController close]; +} + +- (BOOL) isSystemUrl:(NSURL*)url +{ + if ([[url host] isEqualToString:@"itunes.apple.com"]) { + return YES; + } + + return NO; +} + +- (void)open:(CDVInvokedUrlCommand*)command +{ + CDVPluginResult* pluginResult; + + NSString* url = [command argumentAtIndex:0]; + NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf]; + NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]]; + + self.callbackId = command.callbackId; + + if (url != nil) { +#ifdef __CORDOVA_4_0_0 + NSURL* baseUrl = [self.webViewEngine URL]; +#else + NSURL* baseUrl = [self.webView.request URL]; +#endif + NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL]; + + if ([self isSystemUrl:absoluteUrl]) { + target = kInAppBrowserTargetSystem; + } + + if ([target isEqualToString:kInAppBrowserTargetSelf]) { + [self openInCordovaWebView:absoluteUrl withOptions:options]; + } else if ([target isEqualToString:kInAppBrowserTargetSystem]) { + [self openInSystem:absoluteUrl]; + } else { // _blank or anything else + [self openInInAppBrowser:absoluteUrl withOptions:options]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"]; + } + + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options +{ + CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options]; + + if (browserOptions.clearcache) { + NSHTTPCookie *cookie; + NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (cookie in [storage cookies]) + { + if (![cookie.domain isEqual: @".^filecookies^"]) { + [storage deleteCookie:cookie]; + } + } + } + + if (browserOptions.clearsessioncache) { + NSHTTPCookie *cookie; + NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (cookie in [storage cookies]) + { + if (![cookie.domain isEqual: @".^filecookies^"] && cookie.isSessionOnly) { + [storage deleteCookie:cookie]; + } + } + } + + if (self.inAppBrowserViewController == nil) { + NSString* userAgent = [CDVUserAgentUtil originalUserAgent]; + NSString* overrideUserAgent = [self settingForKey:@"OverrideUserAgent"]; + NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"]; + if(overrideUserAgent){ + userAgent = overrideUserAgent; + } + if(appendUserAgent){ + userAgent = [userAgent stringByAppendingString: appendUserAgent]; + } + self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:userAgent prevUserAgent:[self.commandDelegate userAgent] browserOptions: browserOptions]; + self.inAppBrowserViewController.navigationDelegate = self; + + if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) { + self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController; + } + } + + [self.inAppBrowserViewController showLocationBar:browserOptions.location]; + [self.inAppBrowserViewController showToolBar:browserOptions.toolbar :browserOptions.toolbarposition]; + if (browserOptions.closebuttoncaption != nil) { + [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption]; + } + // Set Presentation Style + UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default + if (browserOptions.presentationstyle != nil) { + if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"pagesheet"]) { + presentationStyle = UIModalPresentationPageSheet; + } else if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"formsheet"]) { + presentationStyle = UIModalPresentationFormSheet; + } + } + self.inAppBrowserViewController.modalPresentationStyle = presentationStyle; + + // Set Transition Style + UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default + if (browserOptions.transitionstyle != nil) { + if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"fliphorizontal"]) { + transitionStyle = UIModalTransitionStyleFlipHorizontal; + } else if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"crossdissolve"]) { + transitionStyle = UIModalTransitionStyleCrossDissolve; + } + } + self.inAppBrowserViewController.modalTransitionStyle = transitionStyle; + + // prevent webView from bouncing + if (browserOptions.disallowoverscroll) { + if ([self.inAppBrowserViewController.webView respondsToSelector:@selector(scrollView)]) { + ((UIScrollView*)[self.inAppBrowserViewController.webView scrollView]).bounces = NO; + } else { + for (id subview in self.inAppBrowserViewController.webView.subviews) { + if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { + ((UIScrollView*)subview).bounces = NO; + } + } + } + } + + // UIWebView options + self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale; + self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction; + self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback; + if (IsAtLeastiOSVersion(@"6.0")) { + self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction; + self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering; + } + + [self.inAppBrowserViewController navigateTo:url]; + if (!browserOptions.hidden) { + [self show:nil]; + } +} + +- (void)show:(CDVInvokedUrlCommand*)command +{ + if (self.inAppBrowserViewController == nil) { + NSLog(@"Tried to show IAB after it was closed."); + return; + } + if (_previousStatusBarStyle != -1) { + NSLog(@"Tried to show IAB while already shown"); + return; + } + + _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; + + __block CDVInAppBrowserNavigationController* nav = [[CDVInAppBrowserNavigationController alloc] + initWithRootViewController:self.inAppBrowserViewController]; + nav.orientationDelegate = self.inAppBrowserViewController; + nav.navigationBarHidden = YES; + nav.modalPresentationStyle = self.inAppBrowserViewController.modalPresentationStyle; + + __weak CDVInAppBrowser* weakSelf = self; + + // Run later to avoid the "took a long time" log message. + dispatch_async(dispatch_get_main_queue(), ^{ + if (weakSelf.inAppBrowserViewController != nil) { + [weakSelf.viewController presentViewController:nav animated:YES completion:nil]; + } + }); +} + +- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options +{ + NSURLRequest* request = [NSURLRequest requestWithURL:url]; + +#ifdef __CORDOVA_4_0_0 + // the webview engine itself will filter for this according to <allow-navigation> policy + // in config.xml for cordova-ios-4.0 + [self.webViewEngine loadRequest:request]; +#else + if ([self.commandDelegate URLIsWhitelisted:url]) { + [self.webView loadRequest:request]; + } else { // this assumes the InAppBrowser can be excepted from the white-list + [self openInInAppBrowser:url withOptions:options]; + } +#endif +} + +- (void)openInSystem:(NSURL*)url +{ + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + [[UIApplication sharedApplication] openURL:url]; +} + +// This is a helper method for the inject{Script|Style}{Code|File} API calls, which +// provides a consistent method for injecting JavaScript code into the document. +// +// If a wrapper string is supplied, then the source string will be JSON-encoded (adding +// quotes) and wrapped using string formatting. (The wrapper string should have a single +// '%@' marker). +// +// If no wrapper is supplied, then the source string is executed directly. + +- (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper +{ + // Ensure an iframe bridge is created to communicate with the CDVInAppBrowserViewController + [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){_cdvIframeBridge=d.getElementById('_cdvIframeBridge');if(!_cdvIframeBridge) {var e = _cdvIframeBridge = d.createElement('iframe');e.id='_cdvIframeBridge'; e.style.display='none';d.body.appendChild(e);}})(document)"]; + + if (jsWrapper != nil) { + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:@[source] options:0 error:nil]; + NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + if (sourceArrayString) { + NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)]; + NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString]; + [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject]; + } + } else { + [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source]; + } +} + +- (void)injectScriptCode:(CDVInvokedUrlCommand*)command +{ + NSString* jsWrapper = nil; + + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { + jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+encodeURIComponent(JSON.stringify([eval(%%@)]));", command.callbackId]; + } + [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; +} + +- (void)injectScriptFile:(CDVInvokedUrlCommand*)command +{ + NSString* jsWrapper; + + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + } else { + jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)"; + } + [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; +} + +- (void)injectStyleCode:(CDVInvokedUrlCommand*)command +{ + NSString* jsWrapper; + + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + } else { + jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)"; + } + [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; +} + +- (void)injectStyleFile:(CDVInvokedUrlCommand*)command +{ + NSString* jsWrapper; + + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + } else { + jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)"; + } + [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; +} + +- (BOOL)isValidCallbackId:(NSString *)callbackId +{ + NSError *err = nil; + // Initialize on first use + if (self.callbackIdPattern == nil) { + self.callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"^InAppBrowser[0-9]{1,10}$" options:0 error:&err]; + if (err != nil) { + // Couldn't initialize Regex; No is safer than Yes. + return NO; + } + } + if ([self.callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) { + return YES; + } + return NO; +} + +/** + * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging + * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no + * other code execution is possible. + * + * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form: + * + * gap-iab://<callbackId>/<arguments> + * + * where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789") + * + * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded + * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION + * is returned if the JSON is invalid. + */ +- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + NSURL* url = request.URL; + BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; + + // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute, + // and the path, if present, should be a JSON-encoded value to pass to the callback. + if ([[url scheme] isEqualToString:@"gap-iab"]) { + NSString* scriptCallbackId = [url host]; + CDVPluginResult* pluginResult = nil; + + if ([self isValidCallbackId:scriptCallbackId]) { + NSString* scriptResult = [url path]; + NSError* __autoreleasing error = nil; + + // The message should be a JSON-encoded array of the result of the script which executed. + if ((scriptResult != nil) && ([scriptResult length] > 1)) { + scriptResult = [scriptResult substringFromIndex:1]; + NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; + if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION]; + } + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId]; + return NO; + } + } + //if is an app store link, let the system handle it, otherwise it fails to load it + else if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) { + [theWebView stopLoading]; + [self openInSystem:url]; + return NO; + } + else if ((self.callbackId != nil) && isTopLevelNavigation) { + // Send a loadstart event for each top-level navigation (includes redirects). + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + } + + return YES; +} + +- (void)webViewDidStartLoad:(UIWebView*)theWebView +{ +} + +- (void)webViewDidFinishLoad:(UIWebView*)theWebView +{ + if (self.callbackId != nil) { + // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected). + NSString* url = [self.inAppBrowserViewController.currentURL absoluteString]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"loadstop", @"url":url}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + } +} + +- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + if (self.callbackId != nil) { + NSString* url = [self.inAppBrowserViewController.currentURL absoluteString]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInteger:error.code], @"message": error.localizedDescription}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + } +} + +- (void)browserExit +{ + if (self.callbackId != nil) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"exit"}]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + self.callbackId = nil; + } + // Set navigationDelegate to nil to ensure no callbacks are received from it. + self.inAppBrowserViewController.navigationDelegate = nil; + // Don't recycle the ViewController since it may be consuming a lot of memory. + // Also - this is required for the PDF/User-Agent bug work-around. + self.inAppBrowserViewController = nil; + + if (IsAtLeastiOSVersion(@"7.0")) { + if (_previousStatusBarStyle != -1) { + [[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle]; + } + } + + _previousStatusBarStyle = -1; // this value was reset before reapplying it. caused statusbar to stay black on ios7 +} + +@end + +#pragma mark CDVInAppBrowserViewController + +@implementation CDVInAppBrowserViewController + +@synthesize currentURL; + +- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions +{ + self = [super init]; + if (self != nil) { + _userAgent = userAgent; + _prevUserAgent = prevUserAgent; + _browserOptions = browserOptions; +#ifdef __CORDOVA_4_0_0 + _webViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:self]; +#else + _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self]; +#endif + + [self createViews]; + } + + return self; +} + +// Prevent crashes on closing windows +-(void)dealloc { + self.webView.delegate = nil; +} + +- (void)createViews +{ + // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included + + CGRect webViewBounds = self.view.bounds; + BOOL toolbarIsAtBottom = ![_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]; + webViewBounds.size.height -= _browserOptions.location ? FOOTER_HEIGHT : TOOLBAR_HEIGHT; + self.webView = [[UIWebView alloc] initWithFrame:webViewBounds]; + + self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + + [self.view addSubview:self.webView]; + [self.view sendSubviewToBack:self.webView]; + + self.webView.delegate = _webViewDelegate; + self.webView.backgroundColor = [UIColor whiteColor]; + + self.webView.clearsContextBeforeDrawing = YES; + self.webView.clipsToBounds = YES; + self.webView.contentMode = UIViewContentModeScaleToFill; + self.webView.multipleTouchEnabled = YES; + self.webView.opaque = YES; + self.webView.scalesPageToFit = NO; + self.webView.userInteractionEnabled = YES; + + self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + self.spinner.alpha = 1.000; + self.spinner.autoresizesSubviews = YES; + self.spinner.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin); + self.spinner.clearsContextBeforeDrawing = NO; + self.spinner.clipsToBounds = NO; + self.spinner.contentMode = UIViewContentModeScaleToFill; + self.spinner.frame = CGRectMake(CGRectGetMidX(self.webView.frame), CGRectGetMidY(self.webView.frame), 20.0, 20.0); + self.spinner.hidden = NO; + self.spinner.hidesWhenStopped = YES; + self.spinner.multipleTouchEnabled = NO; + self.spinner.opaque = NO; + self.spinner.userInteractionEnabled = NO; + [self.spinner stopAnimating]; + + self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)]; + self.closeButton.enabled = YES; + + UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + + UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; + fixedSpaceButton.width = 20; + + float toolbarY = toolbarIsAtBottom ? self.view.bounds.size.height - TOOLBAR_HEIGHT : 0.0; + CGRect toolbarFrame = CGRectMake(0.0, toolbarY, self.view.bounds.size.width, TOOLBAR_HEIGHT); + + self.toolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame]; + self.toolbar.alpha = 1.000; + self.toolbar.autoresizesSubviews = YES; + self.toolbar.autoresizingMask = toolbarIsAtBottom ? (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin) : UIViewAutoresizingFlexibleWidth; + self.toolbar.barStyle = UIBarStyleBlackOpaque; + self.toolbar.clearsContextBeforeDrawing = NO; + self.toolbar.clipsToBounds = NO; + self.toolbar.contentMode = UIViewContentModeScaleToFill; + self.toolbar.hidden = NO; + self.toolbar.multipleTouchEnabled = NO; + self.toolbar.opaque = NO; + self.toolbar.userInteractionEnabled = YES; + + CGFloat labelInset = 5.0; + float locationBarY = toolbarIsAtBottom ? self.view.bounds.size.height - FOOTER_HEIGHT : self.view.bounds.size.height - LOCATIONBAR_HEIGHT; + + self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, locationBarY, self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)]; + self.addressLabel.adjustsFontSizeToFitWidth = NO; + self.addressLabel.alpha = 1.000; + self.addressLabel.autoresizesSubviews = YES; + self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin; + self.addressLabel.backgroundColor = [UIColor clearColor]; + self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; + self.addressLabel.clearsContextBeforeDrawing = YES; + self.addressLabel.clipsToBounds = YES; + self.addressLabel.contentMode = UIViewContentModeScaleToFill; + self.addressLabel.enabled = YES; + self.addressLabel.hidden = NO; + self.addressLabel.lineBreakMode = NSLineBreakByTruncatingTail; + + if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumScaleFactor:")]) { + [self.addressLabel setValue:@(10.0/[UIFont labelFontSize]) forKey:@"minimumScaleFactor"]; + } else if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumFontSize:")]) { + [self.addressLabel setValue:@(10.0) forKey:@"minimumFontSize"]; + } + + self.addressLabel.multipleTouchEnabled = NO; + self.addressLabel.numberOfLines = 1; + self.addressLabel.opaque = NO; + self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0); + self.addressLabel.text = NSLocalizedString(@"Loading...", nil); + self.addressLabel.textAlignment = NSTextAlignmentLeft; + self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000]; + self.addressLabel.userInteractionEnabled = NO; + + NSString* frontArrowString = NSLocalizedString(@"â–º", nil); // create arrow from Unicode char + self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)]; + self.forwardButton.enabled = YES; + self.forwardButton.imageInsets = UIEdgeInsetsZero; + + NSString* backArrowString = NSLocalizedString(@"â—„", nil); // create arrow from Unicode char + self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)]; + self.backButton.enabled = YES; + self.backButton.imageInsets = UIEdgeInsetsZero; + + [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]]; + + self.view.backgroundColor = [UIColor grayColor]; + [self.view addSubview:self.toolbar]; + [self.view addSubview:self.addressLabel]; + [self.view addSubview:self.spinner]; +} + +- (void) setWebViewFrame : (CGRect) frame { + NSLog(@"Setting the WebView's frame to %@", NSStringFromCGRect(frame)); + [self.webView setFrame:frame]; +} + +- (void)setCloseButtonTitle:(NSString*)title +{ + // the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically + // but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one) + self.closeButton = nil; + self.closeButton = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)]; + self.closeButton.enabled = YES; + self.closeButton.tintColor = [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1]; + + NSMutableArray* items = [self.toolbar.items mutableCopy]; + [items replaceObjectAtIndex:0 withObject:self.closeButton]; + [self.toolbar setItems:items]; +} + +- (void)showLocationBar:(BOOL)show +{ + CGRect locationbarFrame = self.addressLabel.frame; + + BOOL toolbarVisible = !self.toolbar.hidden; + + // prevent double show/hide + if (show == !(self.addressLabel.hidden)) { + return; + } + + if (show) { + self.addressLabel.hidden = NO; + + if (toolbarVisible) { + // toolBar at the bottom, leave as is + // put locationBar on top of the toolBar + + CGRect webViewBounds = self.view.bounds; + webViewBounds.size.height -= FOOTER_HEIGHT; + [self setWebViewFrame:webViewBounds]; + + locationbarFrame.origin.y = webViewBounds.size.height; + self.addressLabel.frame = locationbarFrame; + } else { + // no toolBar, so put locationBar at the bottom + + CGRect webViewBounds = self.view.bounds; + webViewBounds.size.height -= LOCATIONBAR_HEIGHT; + [self setWebViewFrame:webViewBounds]; + + locationbarFrame.origin.y = webViewBounds.size.height; + self.addressLabel.frame = locationbarFrame; + } + } else { + self.addressLabel.hidden = YES; + + if (toolbarVisible) { + // locationBar is on top of toolBar, hide locationBar + + // webView take up whole height less toolBar height + CGRect webViewBounds = self.view.bounds; + webViewBounds.size.height -= TOOLBAR_HEIGHT; + [self setWebViewFrame:webViewBounds]; + } else { + // no toolBar, expand webView to screen dimensions + [self setWebViewFrame:self.view.bounds]; + } + } +} + +- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition +{ + CGRect toolbarFrame = self.toolbar.frame; + CGRect locationbarFrame = self.addressLabel.frame; + + BOOL locationbarVisible = !self.addressLabel.hidden; + + // prevent double show/hide + if (show == !(self.toolbar.hidden)) { + return; + } + + if (show) { + self.toolbar.hidden = NO; + CGRect webViewBounds = self.view.bounds; + + if (locationbarVisible) { + // locationBar at the bottom, move locationBar up + // put toolBar at the bottom + webViewBounds.size.height -= FOOTER_HEIGHT; + locationbarFrame.origin.y = webViewBounds.size.height; + self.addressLabel.frame = locationbarFrame; + self.toolbar.frame = toolbarFrame; + } else { + // no locationBar, so put toolBar at the bottom + CGRect webViewBounds = self.view.bounds; + webViewBounds.size.height -= TOOLBAR_HEIGHT; + self.toolbar.frame = toolbarFrame; + } + + if ([toolbarPosition isEqualToString:kInAppBrowserToolbarBarPositionTop]) { + toolbarFrame.origin.y = 0; + webViewBounds.origin.y += toolbarFrame.size.height; + [self setWebViewFrame:webViewBounds]; + } else { + toolbarFrame.origin.y = (webViewBounds.size.height + LOCATIONBAR_HEIGHT); + } + [self setWebViewFrame:webViewBounds]; + + } else { + self.toolbar.hidden = YES; + + if (locationbarVisible) { + // locationBar is on top of toolBar, hide toolBar + // put locationBar at the bottom + + // webView take up whole height less locationBar height + CGRect webViewBounds = self.view.bounds; + webViewBounds.size.height -= LOCATIONBAR_HEIGHT; + [self setWebViewFrame:webViewBounds]; + + // move locationBar down + locationbarFrame.origin.y = webViewBounds.size.height; + self.addressLabel.frame = locationbarFrame; + } else { + // no locationBar, expand webView to screen dimensions + [self setWebViewFrame:self.view.bounds]; + } + } +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; +} + +- (void)viewDidUnload +{ + [self.webView loadHTMLString:nil baseURL:nil]; + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; + [super viewDidUnload]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + return UIStatusBarStyleDefault; +} + +- (BOOL)prefersStatusBarHidden { + return NO; +} + +- (void)close +{ + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; + self.currentURL = nil; + + if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) { + [self.navigationDelegate browserExit]; + } + + __weak UIViewController* weakSelf = self; + + // Run later to avoid the "took a long time" log message. + dispatch_async(dispatch_get_main_queue(), ^{ + if ([weakSelf respondsToSelector:@selector(presentingViewController)]) { + [[weakSelf presentingViewController] dismissViewControllerAnimated:YES completion:nil]; + } else { + [[weakSelf parentViewController] dismissViewControllerAnimated:YES completion:nil]; + } + }); +} + +- (void)navigateTo:(NSURL*)url +{ + NSURLRequest* request = [NSURLRequest requestWithURL:url]; + + if (_userAgentLockToken != 0) { + [self.webView loadRequest:request]; + } else { + __weak CDVInAppBrowserViewController* weakSelf = self; + [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) { + _userAgentLockToken = lockToken; + [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken]; + [weakSelf.webView loadRequest:request]; + }]; + } +} + +- (void)goBack:(id)sender +{ + [self.webView goBack]; +} + +- (void)goForward:(id)sender +{ + [self.webView goForward]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + if (IsAtLeastiOSVersion(@"7.0")) { + [[UIApplication sharedApplication] setStatusBarStyle:[self preferredStatusBarStyle]]; + } + [self rePositionViews]; + + [super viewWillAppear:animated]; +} + +// +// On iOS 7 the status bar is part of the view's dimensions, therefore it's height has to be taken into account. +// The height of it could be hardcoded as 20 pixels, but that would assume that the upcoming releases of iOS won't +// change that value. +// +- (float) getStatusBarOffset { + CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame]; + float statusBarOffset = IsAtLeastiOSVersion(@"7.0") ? MIN(statusBarFrame.size.width, statusBarFrame.size.height) : 0.0; + return statusBarOffset; +} + +- (void) rePositionViews { + if ([_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]) { + [self.webView setFrame:CGRectMake(self.webView.frame.origin.x, TOOLBAR_HEIGHT, self.webView.frame.size.width, self.webView.frame.size.height)]; + [self.toolbar setFrame:CGRectMake(self.toolbar.frame.origin.x, [self getStatusBarOffset], self.toolbar.frame.size.width, self.toolbar.frame.size.height)]; + } +} + +#pragma mark UIWebViewDelegate + +- (void)webViewDidStartLoad:(UIWebView*)theWebView +{ + // loading url, start spinner, update back/forward + + self.addressLabel.text = NSLocalizedString(@"Loading...", nil); + self.backButton.enabled = theWebView.canGoBack; + self.forwardButton.enabled = theWebView.canGoForward; + + [self.spinner startAnimating]; + + return [self.navigationDelegate webViewDidStartLoad:theWebView]; +} + +- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; + + if (isTopLevelNavigation) { + self.currentURL = request.URL; + } + return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} + +- (void)webViewDidFinishLoad:(UIWebView*)theWebView +{ + // update url, stop spinner, update back/forward + + self.addressLabel.text = [self.currentURL absoluteString]; + self.backButton.enabled = theWebView.canGoBack; + self.forwardButton.enabled = theWebView.canGoForward; + + [self.spinner stopAnimating]; + + // Work around a bug where the first time a PDF is opened, all UIWebViews + // reload their User-Agent from NSUserDefaults. + // This work-around makes the following assumptions: + // 1. The app has only a single Cordova Webview. If not, then the app should + // take it upon themselves to load a PDF in the background as a part of + // their start-up flow. + // 2. That the PDF does not require any additional network requests. We change + // the user-agent here back to that of the CDVViewController, so requests + // from it must pass through its white-list. This *does* break PDFs that + // contain links to other remote PDF/websites. + // More info at https://issues.apache.org/jira/browse/CB-2225 + BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]]; + if (isPDF) { + [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken]; + } + + [self.navigationDelegate webViewDidFinishLoad:theWebView]; +} + +- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + // log fail message, stop spinner, update back/forward + NSLog(@"webView:didFailLoadWithError - %ld: %@", (long)error.code, [error localizedDescription]); + + self.backButton.enabled = theWebView.canGoBack; + self.forwardButton.enabled = theWebView.canGoForward; + [self.spinner stopAnimating]; + + self.addressLabel.text = NSLocalizedString(@"Load Error", nil); + + [self.navigationDelegate webView:theWebView didFailLoadWithError:error]; +} + +#pragma mark CDVScreenOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return 1 << UIInterfaceOrientationPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + +@end + +@implementation CDVInAppBrowserOptions + +- (id)init +{ + if (self = [super init]) { + // default values + self.location = YES; + self.toolbar = YES; + self.closebuttoncaption = nil; + self.toolbarposition = kInAppBrowserToolbarBarPositionBottom; + self.clearcache = NO; + self.clearsessioncache = NO; + + self.enableviewportscale = NO; + self.mediaplaybackrequiresuseraction = NO; + self.allowinlinemediaplayback = NO; + self.keyboarddisplayrequiresuseraction = YES; + self.suppressesincrementalrendering = NO; + self.hidden = NO; + self.disallowoverscroll = NO; + } + + return self; +} + ++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options +{ + CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init]; + + // NOTE: this parsing does not handle quotes within values + NSArray* pairs = [options componentsSeparatedByString:@","]; + + // parse keys and values, set the properties + for (NSString* pair in pairs) { + NSArray* keyvalue = [pair componentsSeparatedByString:@"="]; + + if ([keyvalue count] == 2) { + NSString* key = [[keyvalue objectAtIndex:0] lowercaseString]; + NSString* value = [keyvalue objectAtIndex:1]; + NSString* value_lc = [value lowercaseString]; + + BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"]; + NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setAllowsFloats:YES]; + BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil; + + // set the property according to the key name + if ([obj respondsToSelector:NSSelectorFromString(key)]) { + if (isNumber) { + [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key]; + } else if (isBoolean) { + [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key]; + } else { + [obj setValue:value forKey:key]; + } + } + } + } + + return obj; +} + +@end + +@implementation CDVInAppBrowserNavigationController : UINavigationController + +- (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { + if ( self.presentedViewController) { + [super dismissViewControllerAnimated:flag completion:completion]; + } +} + +- (void) viewDidLoad { + + CGRect frame = [UIApplication sharedApplication].statusBarFrame; + + // simplified from: http://stackoverflow.com/a/25669695/219684 + + UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:[self invertFrameIfNeeded:frame]]; + bgToolbar.barStyle = UIBarStyleDefault; + [bgToolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [self.view addSubview:bgToolbar]; + + [super viewDidLoad]; +} + +- (CGRect) invertFrameIfNeeded:(CGRect)rect { + // We need to invert since on iOS 7 frames are always in Portrait context + if (!IsAtLeastiOSVersion(@"8.0")) { + if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { + CGFloat temp = rect.size.width; + rect.size.width = rect.size.height; + rect.size.height = temp; + } + rect.origin = CGPointZero; + } + return rect; +} + +#pragma mark CDVScreenOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return 1 << UIInterfaceOrientationPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + + +@end + diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h new file mode 100644 index 00000000..8add0279 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h @@ -0,0 +1,34 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Foundation/Foundation.h> +#import <Cordova/CDVPlugin.h> +#import "CDVReachability.h" + +@interface CDVConnection : CDVPlugin { + NSString* type; + NSString* _callbackId; + + CDVReachability* internetReach; +} + +@property (copy) NSString* connectionType; +@property (strong) CDVReachability* internetReach; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m new file mode 100644 index 00000000..6715322a --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m @@ -0,0 +1,156 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +#import <CoreTelephony/CTTelephonyNetworkInfo.h> + +#import "CDVConnection.h" +#import "CDVReachability.h" + +@interface CDVConnection (PrivateMethods) +- (void)updateOnlineStatus; +- (void)sendPluginResult; +@end + +@implementation CDVConnection + +@synthesize connectionType, internetReach; + +- (void)getConnectionInfo:(CDVInvokedUrlCommand*)command +{ + _callbackId = command.callbackId; + [self sendPluginResult]; +} + +- (void)sendPluginResult +{ + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:self.connectionType]; + + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:_callbackId]; +} + +- (NSString*)w3cConnectionTypeFor:(CDVReachability*)reachability +{ + NetworkStatus networkStatus = [reachability currentReachabilityStatus]; + + switch (networkStatus) { + case NotReachable: + return @"none"; + + case ReachableViaWWAN: + { + BOOL isConnectionRequired = [reachability connectionRequired]; + if (isConnectionRequired) { + return @"none"; + } else { + if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0" options:NSNumericSearch] != NSOrderedAscending) { + CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new]; + if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) { + return @"2g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge]) { + return @"2g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) { + return @"3g"; + } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) { + return @"4g"; + } + } + return @"cellular"; + } + } + case ReachableViaWiFi: + return @"wifi"; + + default: + return @"unknown"; + } +} + +- (BOOL)isCellularConnection:(NSString*)theConnectionType +{ + return [theConnectionType isEqualToString:@"2g"] || + [theConnectionType isEqualToString:@"3g"] || + [theConnectionType isEqualToString:@"4g"] || + [theConnectionType isEqualToString:@"cellular"]; +} + +- (void)updateReachability:(CDVReachability*)reachability +{ + if (reachability) { + // check whether the connection type has changed + NSString* newConnectionType = [self w3cConnectionTypeFor:reachability]; + if ([newConnectionType isEqualToString:self.connectionType]) { // the same as before, remove dupes + return; + } else { + self.connectionType = [self w3cConnectionTypeFor:reachability]; + } + } + [self sendPluginResult]; +} + +- (void)updateConnectionType:(NSNotification*)note +{ + CDVReachability* curReach = [note object]; + + if ((curReach != nil) && [curReach isKindOfClass:[CDVReachability class]]) { + [self updateReachability:curReach]; + } +} + +- (void)onPause +{ + [self.internetReach stopNotifier]; +} + +- (void)onResume +{ + [self.internetReach startNotifier]; + [self updateReachability:self.internetReach]; +} + +- (void)pluginInitialize +{ + self.connectionType = @"none"; + self.internetReach = [CDVReachability reachabilityForInternetConnection]; + self.connectionType = [self w3cConnectionTypeFor:self.internetReach]; + [self.internetReach startNotifier]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateConnectionType:) + name:kReachabilityChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateConnectionType:) + name:CTRadioAccessTechnologyDidChangeNotification object:nil]; + if (UIApplicationDidEnterBackgroundNotification && UIApplicationWillEnterForegroundNotification) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; + } +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h new file mode 100644 index 00000000..01a95c35 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h @@ -0,0 +1,85 @@ +/* + + File: Reachability.h + Abstract: Basic demonstration of how to use the SystemConfiguration Reachability APIs. + Version: 2.2 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + +#import <Foundation/Foundation.h> +#import <SystemConfiguration/SystemConfiguration.h> +#import <netinet/in.h> + +typedef enum { + NotReachable = 0, + ReachableViaWWAN, // this value has been swapped with ReachableViaWiFi for Cordova backwards compat. reasons + ReachableViaWiFi // this value has been swapped with ReachableViaWWAN for Cordova backwards compat. reasons +} NetworkStatus; +#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" + +@interface CDVReachability : NSObject +{ + BOOL localWiFiRef; + SCNetworkReachabilityRef reachabilityRef; +} + +// reachabilityWithHostName- Use to check the reachability of a particular host name. ++ (CDVReachability*)reachabilityWithHostName:(NSString*)hostName; + +// reachabilityWithAddress- Use to check the reachability of a particular IP address. ++ (CDVReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress; + +// reachabilityForInternetConnection- checks whether the default route is available. +// Should be used by applications that do not connect to a particular host ++ (CDVReachability*)reachabilityForInternetConnection; + +// reachabilityForLocalWiFi- checks whether a local wifi connection is available. ++ (CDVReachability*)reachabilityForLocalWiFi; + +// Start listening for reachability notifications on the current run loop +- (BOOL)startNotifier; +- (void)stopNotifier; + +- (NetworkStatus)currentReachabilityStatus; +// WWAN may be available, but not active until a connection has been established. +// WiFi may require a connection for VPN on Demand. +- (BOOL)connectionRequired; +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m new file mode 100644 index 00000000..1399867e --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m @@ -0,0 +1,266 @@ +/* + + File: Reachability.m + Abstract: Basic demonstration of how to use the SystemConfiguration Reachability APIs. + Version: 2.2 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + +#import <sys/socket.h> +#import <netinet/in.h> +#import <netinet6/in6.h> +#import <arpa/inet.h> +#import <ifaddrs.h> +#import <netdb.h> + +#import <CoreFoundation/CoreFoundation.h> + +#import "CDVReachability.h" + +#define kShouldPrintReachabilityFlags 0 + +static void CDVPrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) +{ +#if kShouldPrintReachabilityFlags + NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', + comment + ); +#endif +} + +@implementation CDVReachability + +static void CDVReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + // NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + // NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); + + // Converted the asserts above to conditionals, with safe return from the function + if (info == NULL) { + NSLog(@"info was NULL in ReachabilityCallback"); + return; + } + + if (![(__bridge NSObject*)info isKindOfClass :[CDVReachability class]]) { + NSLog(@"info was wrong class in ReachabilityCallback"); + return; + } + + // We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively + // in case someon uses the Reachability object in a different thread. + @autoreleasepool { + CDVReachability* noteObject = (__bridge CDVReachability*)info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject]; + } +} + +- (BOOL)startNotifier +{ + BOOL retVal = NO; + SCNetworkReachabilityContext context = {0, (__bridge void*)(self), NULL, NULL, NULL}; + + if (SCNetworkReachabilitySetCallback(reachabilityRef, CDVReachabilityCallback, &context)) { + if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + retVal = YES; + } + } + return retVal; +} + +- (void)stopNotifier +{ + if (reachabilityRef != NULL) { + SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + +- (void)dealloc +{ + [self stopNotifier]; + if (reachabilityRef != NULL) { + CFRelease(reachabilityRef); + } +} + ++ (CDVReachability*)reachabilityWithHostName:(NSString*)hostName; +{ + CDVReachability* retVal = NULL; + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); + if (reachability != NULL) { + retVal = [[self alloc] init]; + if (retVal != NULL) { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + else { + CFRelease(reachability); + } + } + return retVal; +} + ++ (CDVReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress; +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + CDVReachability* retVal = NULL; + if (reachability != NULL) { + retVal = [[self alloc] init]; + if (retVal != NULL) { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + else { + CFRelease(reachability); + } + } + return retVal; +} + ++ (CDVReachability*)reachabilityForInternetConnection; +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + return [self reachabilityWithAddress:&zeroAddress]; +} + ++ (CDVReachability*)reachabilityForLocalWiFi; +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + CDVReachability* retVal = [self reachabilityWithAddress:&localWifiAddress]; + if (retVal != NULL) { + retVal->localWiFiRef = YES; + } + return retVal; +} + +#pragma mark Network Flag Handling + +- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags +{ + CDVPrintReachabilityFlags(flags, "localWiFiStatusForFlags"); + + BOOL retVal = NotReachable; + if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) { + retVal = ReachableViaWiFi; + } + return retVal; +} + +- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags +{ + CDVPrintReachabilityFlags(flags, "networkStatusForFlags"); + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) { + // if target host is not reachable + return NotReachable; + } + + NetworkStatus retVal = NotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) { + // if target host is reachable and no connection is required + // then we'll assume (for now) that your on Wi-Fi + retVal = ReachableViaWiFi; + } + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) || + ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))) { + // ... and the connection is on-demand (or on-traffic) if the + // calling application is using the CFSocketStream or higher APIs + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) { + // ... and no [user] intervention is needed + retVal = ReachableViaWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) { + // ... but WWAN connections are OK if the calling application + // is using the CFNetwork (CFSocketStream?) APIs. + retVal = ReachableViaWWAN; + } + return retVal; +} + +- (BOOL)connectionRequired; +{ + NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { + return flags & kSCNetworkReachabilityFlagsConnectionRequired; + } + return NO; +} + +- (NetworkStatus)currentReachabilityStatus +{ + NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); + NetworkStatus retVal = NotReachable; + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { + if (localWiFiRef) { + retVal = [self localWiFiStatusForFlags:flags]; + } else { + retVal = [self networkStatusForFlags:flags]; + } + } + return retVal; +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h new file mode 100644 index 00000000..ec5d6022 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h @@ -0,0 +1,45 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Foundation/Foundation.h> +#import <Cordova/CDVPlugin.h> + +typedef struct { + BOOL iPhone; + BOOL iPad; + BOOL iPhone4; + BOOL iPhone5; + BOOL iPhone6; + BOOL iPhone6Plus; + BOOL retina; + +} CDV_iOSDevice; + +@interface CDVSplashScreen : CDVPlugin { + UIActivityIndicatorView* _activityView; + UIImageView* _imageView; + NSString* _curImageName; + BOOL _visible; + BOOL _destroyed; +} + +- (void)show:(CDVInvokedUrlCommand*)command; +- (void)hide:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m new file mode 100644 index 00000000..8ad8116b --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m @@ -0,0 +1,483 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVSplashScreen.h" +#import <Cordova/CDVViewController.h> +#import <Cordova/CDVScreenOrientationDelegate.h> +#import "CDVViewController+SplashScreen.h" + +#define kSplashScreenDurationDefault 3000.0f +#define kFadeDurationDefault 500.0f + + +@implementation CDVSplashScreen + +- (void)pluginInitialize +{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil]; + + [self setVisible:YES]; +} + +- (void)show:(CDVInvokedUrlCommand*)command +{ + [self setVisible:YES]; +} + +- (void)hide:(CDVInvokedUrlCommand*)command +{ + [self setVisible:NO andForce:YES]; +} + +- (void)pageDidLoad +{ + id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]]; + + // if value is missing, default to yes + if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) { + [self setVisible:NO]; + } +} + +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context +{ + [self updateImage]; +} + +- (void)createViews +{ + /* + * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style. + * + * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge + * white = UIActivityIndicatorViewStyleWhite + * gray = UIActivityIndicatorViewStyleGray + * + */ + + // Determine whether rotation should be enabled for this device + // Per iOS HIG, landscape is only supported on iPad and iPhone 6+ + CDV_iOSDevice device = [self getCurrentDevice]; + BOOL autorotateValue = (device.iPad || device.iPhone6Plus) ? + [(CDVViewController *)self.viewController shouldAutorotateDefaultValue] : + NO; + + [(CDVViewController *)self.viewController setEnabledAutorotation:autorotateValue]; + + NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:[@"TopActivityIndicator" lowercaseString]]; + UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray; + + if ([topActivityIndicator isEqualToString:@"whiteLarge"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge; + } + else if ([topActivityIndicator isEqualToString:@"white"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite; + } + else if ([topActivityIndicator isEqualToString:@"gray"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray; + } + + UIView* parentView = self.viewController.view; + parentView.userInteractionEnabled = NO; // disable user interaction while splashscreen is shown + _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle]; + _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2); + _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin + | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; + [_activityView startAnimating]; + + // Set the frame & image later. + _imageView = [[UIImageView alloc] init]; + [parentView addSubview:_imageView]; + + id showSplashScreenSpinnerValue = [self.commandDelegate.settings objectForKey:[@"ShowSplashScreenSpinner" lowercaseString]]; + // backwards compatibility - if key is missing, default to true + if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue]) + { + [parentView addSubview:_activityView]; + } + + // Frame is required when launching in portrait mode. + // Bounds for landscape since it captures the rotation. + [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil]; + [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil]; + + [self updateImage]; + _destroyed = NO; +} + +- (void)hideViews +{ + [_imageView setAlpha:0]; + [_activityView setAlpha:0]; +} + +- (void)destroyViews +{ + _destroyed = YES; + [(CDVViewController *)self.viewController setEnabledAutorotation:[(CDVViewController *)self.viewController shouldAutorotateDefaultValue]]; + + [_imageView removeFromSuperview]; + [_activityView removeFromSuperview]; + _imageView = nil; + _activityView = nil; + _curImageName = nil; + + self.viewController.view.userInteractionEnabled = YES; // re-enable user interaction upon completion + @try { + [self.viewController.view removeObserver:self forKeyPath:@"frame"]; + [self.viewController.view removeObserver:self forKeyPath:@"bounds"]; + } + @catch (NSException *exception) { + // When reloading the page from a remotely connected Safari, there + // are no observers, so the removeObserver method throws an exception, + // that we can safely ignore. + // Alternatively we can check whether there are observers before calling removeObserver + } +} + +- (CDV_iOSDevice) getCurrentDevice +{ + CDV_iOSDevice device; + + UIScreen* mainScreen = [UIScreen mainScreen]; + CGFloat mainScreenHeight = mainScreen.bounds.size.height; + CGFloat mainScreenWidth = mainScreen.bounds.size.width; + + int limit = MAX(mainScreenHeight,mainScreenWidth); + + device.iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); + device.iPhone = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone); + device.retina = ([mainScreen scale] == 2.0); + device.iPhone4 = (device.iPhone && limit == 480.0); + device.iPhone5 = (device.iPhone && limit == 568.0); + // note these below is not a true device detect, for example if you are on an + // iPhone 6/6+ but the app is scaled it will prob set iPhone5 as true, but + // this is appropriate for detecting the runtime screen environment + device.iPhone6 = (device.iPhone && limit == 667.0); + device.iPhone6Plus = (device.iPhone && limit == 736.0); + + return device; +} + +- (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id<CDVScreenOrientationDelegate>)orientationDelegate device:(CDV_iOSDevice)device +{ + // Use UILaunchImageFile if specified in plist. Otherwise, use Default. + NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"]; + + NSUInteger supportedOrientations = [orientationDelegate supportedInterfaceOrientations]; + + // Checks to see if the developer has locked the orientation to use only one of Portrait or Landscape + BOOL supportsLandscape = (supportedOrientations & UIInterfaceOrientationMaskLandscape); + BOOL supportsPortrait = (supportedOrientations & UIInterfaceOrientationMaskPortrait || supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown); + // this means there are no mixed orientations in there + BOOL isOrientationLocked = !(supportsPortrait && supportsLandscape); + + if (imageName) + { + imageName = [imageName stringByDeletingPathExtension]; + } + else + { + imageName = @"Default"; + } + + // Add Asset Catalog specific prefixes + if ([imageName isEqualToString:@"LaunchImage"]) + { + if (device.iPhone4 || device.iPhone5 || device.iPad) { + imageName = [imageName stringByAppendingString:@"-700"]; + } else if(device.iPhone6) { + imageName = [imageName stringByAppendingString:@"-800"]; + } else if(device.iPhone6Plus) { + imageName = [imageName stringByAppendingString:@"-800"]; + if (currentOrientation == UIInterfaceOrientationPortrait || currentOrientation == UIInterfaceOrientationPortraitUpsideDown) + { + imageName = [imageName stringByAppendingString:@"-Portrait"]; + } + } + } + + if (device.iPhone5) + { // does not support landscape + imageName = [imageName stringByAppendingString:@"-568h"]; + } + else if (device.iPhone6) + { // does not support landscape + imageName = [imageName stringByAppendingString:@"-667h"]; + } + else if (device.iPhone6Plus) + { // supports landscape + if (isOrientationLocked) + { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"")]; + } + else + { + switch (currentOrientation) + { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + default: + break; + } + } + imageName = [imageName stringByAppendingString:@"-736h"]; + + } + else if (device.iPad) + { // supports landscape + if (isOrientationLocked) + { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"-Portrait")]; + } + else + { + switch (currentOrientation) + { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + default: + imageName = [imageName stringByAppendingString:@"-Portrait"]; + break; + } + } + } + + return imageName; +} + +- (UIInterfaceOrientation)getCurrentOrientation +{ + UIInterfaceOrientation iOrientation = [UIApplication sharedApplication].statusBarOrientation; + UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation; + + bool landscape; + + if (dOrientation == UIDeviceOrientationUnknown || dOrientation == UIDeviceOrientationFaceUp || dOrientation == UIDeviceOrientationFaceDown) { + // If the device is laying down, use the UIInterfaceOrientation based on the status bar. + landscape = UIInterfaceOrientationIsLandscape(iOrientation); + } else { + // If the device is not laying down, use UIDeviceOrientation. + landscape = UIDeviceOrientationIsLandscape(dOrientation); + + // There's a bug in iOS!!!! http://openradar.appspot.com/7216046 + // So values needs to be reversed for landscape! + if (dOrientation == UIDeviceOrientationLandscapeLeft) + { + iOrientation = UIInterfaceOrientationLandscapeRight; + } + else if (dOrientation == UIDeviceOrientationLandscapeRight) + { + iOrientation = UIInterfaceOrientationLandscapeLeft; + } + else if (dOrientation == UIDeviceOrientationPortrait) + { + iOrientation = UIInterfaceOrientationPortrait; + } + else if (dOrientation == UIDeviceOrientationPortraitUpsideDown) + { + iOrientation = UIInterfaceOrientationPortraitUpsideDown; + } + } + + return iOrientation; +} + +// Sets the view's frame and image. +- (void)updateImage +{ + NSString* imageName = [self getImageName:[self getCurrentOrientation] delegate:(id<CDVScreenOrientationDelegate>)self.viewController device:[self getCurrentDevice]]; + + if (![imageName isEqualToString:_curImageName]) + { + UIImage* img = [UIImage imageNamed:imageName]; + _imageView.image = img; + _curImageName = imageName; + } + + // Check that splash screen's image exists before updating bounds + if (_imageView.image) + { + [self updateBounds]; + } + else + { + NSLog(@"WARNING: The splashscreen image named %@ was not found", imageName); + } +} + +- (void)updateBounds +{ + UIImage* img = _imageView.image; + CGRect imgBounds = (img) ? CGRectMake(0, 0, img.size.width, img.size.height) : CGRectZero; + + CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size; + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + CGAffineTransform imgTransform = CGAffineTransformIdentity; + + /* If and only if an iPhone application is landscape-only as per + * UISupportedInterfaceOrientations, the view controller's orientation is + * landscape. In this case the image must be rotated in order to appear + * correctly. + */ + CDV_iOSDevice device = [self getCurrentDevice]; + if (UIInterfaceOrientationIsLandscape(orientation) && !device.iPhone6Plus && !device.iPad) + { + imgTransform = CGAffineTransformMakeRotation(M_PI / 2); + imgBounds.size = CGSizeMake(imgBounds.size.height, imgBounds.size.width); + } + + // There's a special case when the image is the size of the screen. + if (CGSizeEqualToSize(screenSize, imgBounds.size)) + { + CGRect statusFrame = [self.viewController.view convertRect:[UIApplication sharedApplication].statusBarFrame fromView:nil]; + if (!(IsAtLeastiOSVersion(@"7.0"))) + { + imgBounds.origin.y -= statusFrame.size.height; + } + } + else if (imgBounds.size.width > 0) + { + CGRect viewBounds = self.viewController.view.bounds; + CGFloat imgAspect = imgBounds.size.width / imgBounds.size.height; + CGFloat viewAspect = viewBounds.size.width / viewBounds.size.height; + // This matches the behaviour of the native splash screen. + CGFloat ratio; + if (viewAspect > imgAspect) + { + ratio = viewBounds.size.width / imgBounds.size.width; + } + else + { + ratio = viewBounds.size.height / imgBounds.size.height; + } + imgBounds.size.height *= ratio; + imgBounds.size.width *= ratio; + } + + _imageView.transform = imgTransform; + _imageView.frame = imgBounds; +} + +- (void)setVisible:(BOOL)visible +{ + [self setVisible:visible andForce:NO]; +} + +- (void)setVisible:(BOOL)visible andForce:(BOOL)force +{ + if (visible != _visible || force) + { + _visible = visible; + + id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]]; + id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]]; + + float fadeDuration = fadeSplashScreenDuration == nil ? kFadeDurationDefault : [fadeSplashScreenDuration floatValue]; + + id splashDurationString = [self.commandDelegate.settings objectForKey: [@"SplashScreenDelay" lowercaseString]]; + float splashDuration = splashDurationString == nil ? kSplashScreenDurationDefault : [splashDurationString floatValue]; + + id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]]; + BOOL autoHideSplashScreen = true; + + if (autoHideSplashScreenValue != nil) { + autoHideSplashScreen = [autoHideSplashScreenValue boolValue]; + } + + if (!autoHideSplashScreen) { + // CB-10412 SplashScreenDelay does not make sense if the splashscreen is hidden manually + splashDuration = 0; + } + + + if (fadeSplashScreenValue == nil) + { + fadeSplashScreenValue = @"true"; + } + + if (![fadeSplashScreenValue boolValue]) + { + fadeDuration = 0; + } + else if (fadeDuration < 30) + { + // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10 + // they mean 10 seconds, and not the meaningless 10ms + fadeDuration *= 1000; + } + + if (_visible) + { + if (_imageView == nil) + { + [self createViews]; + } + } + else if (fadeDuration == 0 && splashDuration == 0) + { + [self destroyViews]; + } + else + { + __weak __typeof(self) weakSelf = self; + float effectiveSplashDuration; + + // [CB-10562] AutoHideSplashScreen may be "true" but we should still be able to hide the splashscreen manually. + if (!autoHideSplashScreen || force) { + effectiveSplashDuration = (fadeDuration) / 1000; + } else { + effectiveSplashDuration = (splashDuration - fadeDuration) / 1000; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t) effectiveSplashDuration * NSEC_PER_SEC), dispatch_get_main_queue(), CFBridgingRelease(CFBridgingRetain(^(void) { + if (!_destroyed) { + [UIView transitionWithView:self.viewController.view + duration:(fadeDuration / 1000) + options:UIViewAnimationOptionTransitionNone + animations:^(void) { + [weakSelf hideViews]; + } + completion:^(BOOL finished) { + // Always destroy views, otherwise you could have an + // invisible splashscreen that is overlayed over your active views + // which causes that no touch events are passed + if (!_destroyed) { + [weakSelf destroyViews]; + // TODO: It might also be nice to have a js event happen here -jm + } + } + ]; + } + }))); + } + } +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h new file mode 100644 index 00000000..a948ea31 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h @@ -0,0 +1,28 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVViewController.h> + +@interface CDVViewController (SplashScreen) + +@property (nonatomic, assign) BOOL enabledAutorotation; +@property (nonatomic, readonly) BOOL shouldAutorotateDefaultValue; + + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m new file mode 100644 index 00000000..e483def6 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m @@ -0,0 +1,89 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVViewController+SplashScreen.h" +#import <objc/runtime.h> + +@implementation CDVViewController (SplashScreen) + +@dynamic enabledAutorotation; + +- (void)setEnabledAutorotation:(BOOL)value +{ + objc_setAssociatedObject(self, + @selector(enabledAutorotation), + [NSNumber numberWithBool:value], + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)enabledAutorotation +{ + NSNumber *number = (NSNumber *)objc_getAssociatedObject(self, @selector(enabledAutorotation)); + + // Defaulting to YES to correspond parent CDVViewController behavior + if (number == nil) + { + return YES; + } + + return [number boolValue]; +} + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class class = [self class]; + + SEL originalSelector = @selector(shouldAutorotate); + SEL swizzledSelector = @selector(splash_shouldAutorotate); + + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + BOOL didAddMethod = class_addMethod(class, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(class, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + }); +} + +#pragma mark - Method Swizzling + +- (BOOL)splash_shouldAutorotate +{ + return self.enabledAutorotation; +} + + +- (BOOL)shouldAutorotateDefaultValue +{ + return [self splash_shouldAutorotate]; +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h new file mode 100644 index 00000000..0be08cc3 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h @@ -0,0 +1,50 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVPlugin.h> +#import <Cordova/CDVInvokedUrlCommand.h> + +@interface CDVStatusBar : CDVPlugin { + @protected + BOOL _statusBarOverlaysWebView; + UIView* _statusBarBackgroundView; + BOOL _uiviewControllerBasedStatusBarAppearance; + UIColor* _statusBarBackgroundColor; + NSString* _eventsCallbackId; +} + +@property (atomic, assign) BOOL statusBarOverlaysWebView; +@property (atomic, assign) BOOL statusBarVisible; + +- (void) overlaysWebView:(CDVInvokedUrlCommand*)command; + +- (void) styleDefault:(CDVInvokedUrlCommand*)command; +- (void) styleLightContent:(CDVInvokedUrlCommand*)command; +- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command; +- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command; + +- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command; +- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command; + +- (void) hide:(CDVInvokedUrlCommand*)command; +- (void) show:(CDVInvokedUrlCommand*)command; + +- (void) _ready:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m new file mode 100644 index 00000000..58fbc296 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m @@ -0,0 +1,501 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +/* + NOTE: plugman/cordova cli should have already installed this, + but you need the value UIViewControllerBasedStatusBarAppearance + in your Info.plist as well to set the styles in iOS 7 + */ + +#import "CDVStatusBar.h" +#import <objc/runtime.h> +#import <Cordova/CDVViewController.h> + +static const void *kHideStatusBar = &kHideStatusBar; +static const void *kStatusBarStyle = &kStatusBarStyle; + +@interface CDVViewController (StatusBar) + +@property (nonatomic, retain) id sb_hideStatusBar; +@property (nonatomic, retain) id sb_statusBarStyle; + +@end + +@implementation CDVViewController (StatusBar) + +@dynamic sb_hideStatusBar; +@dynamic sb_statusBarStyle; + +- (id)sb_hideStatusBar { + return objc_getAssociatedObject(self, kHideStatusBar); +} + +- (void)setSb_hideStatusBar:(id)newHideStatusBar { + objc_setAssociatedObject(self, kHideStatusBar, newHideStatusBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (id)sb_statusBarStyle { + return objc_getAssociatedObject(self, kStatusBarStyle); +} + +- (void)setSb_statusBarStyle:(id)newStatusBarStyle { + objc_setAssociatedObject(self, kStatusBarStyle, newStatusBarStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL) prefersStatusBarHidden { + return [self.sb_hideStatusBar boolValue]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + return (UIStatusBarStyle)[self.sb_statusBarStyle intValue]; +} + +@end + + +@interface CDVStatusBar () <UIScrollViewDelegate> +- (void)fireTappedEvent; +- (void)updateIsVisible:(BOOL)visible; +@end + +@implementation CDVStatusBar + +- (id)settingForKey:(NSString*)key +{ + return [self.commandDelegate.settings objectForKey:[key lowercaseString]]; +} + +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context +{ + if ([keyPath isEqual:@"statusBarHidden"]) { + NSNumber* newValue = [change objectForKey:NSKeyValueChangeNewKey]; + [self updateIsVisible:![newValue boolValue]]; + } +} + +-(void)cordovaViewWillAppear:(NSNotification*)notification +{ + [self resizeWebView]; +} + +-(void)statusBarDidChangeFrame:(NSNotification*)notification +{ + //add a small delay for iOS 7 ( 0.1 seconds ) + __weak CDVStatusBar* weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [weakSelf resizeWebView]; + }); +} + +- (void)pluginInitialize +{ + BOOL isiOS7 = (IsAtLeastiOSVersion(@"7.0")); + + // init + NSNumber* uiviewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]; + _uiviewControllerBasedStatusBarAppearance = (uiviewControllerBasedStatusBarAppearance == nil || [uiviewControllerBasedStatusBarAppearance boolValue]) && isiOS7; + + // observe the statusBarHidden property + [[UIApplication sharedApplication] addObserver:self forKeyPath:@"statusBarHidden" options:NSKeyValueObservingOptionNew context:NULL]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarDidChangeFrame:) name: UIApplicationDidChangeStatusBarFrameNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cordovaViewWillAppear:) name: @"CDVViewWillAppearNotification" object:nil]; + + _statusBarOverlaysWebView = YES; // default + + [self initializeStatusBarBackgroundView]; + + self.viewController.view.autoresizesSubviews = YES; + + NSString* setting; + + setting = @"StatusBarBackgroundColor"; + if ([self settingForKey:setting]) { + [self _backgroundColorByHexString:[self settingForKey:setting]]; + } + + setting = @"StatusBarStyle"; + if ([self settingForKey:setting]) { + [self setStatusBarStyle:[self settingForKey:setting]]; + } + + // blank scroll view to intercept status bar taps + self.webView.scrollView.scrollsToTop = NO; + UIScrollView *fakeScrollView = [[UIScrollView alloc] initWithFrame:UIScreen.mainScreen.bounds]; + fakeScrollView.delegate = self; + fakeScrollView.scrollsToTop = YES; + [self.viewController.view addSubview:fakeScrollView]; // Add scrollview to the view heirarchy so that it will begin accepting status bar taps + [self.viewController.view sendSubviewToBack:fakeScrollView]; // Send it to the very back of the view heirarchy + fakeScrollView.contentSize = CGSizeMake(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height * 2.0f); // Make the scroll view longer than the screen itself + fakeScrollView.contentOffset = CGPointMake(0.0f, UIScreen.mainScreen.bounds.size.height); // Scroll down so a tap will take scroll view back to the top + + _statusBarVisible = ![UIApplication sharedApplication].isStatusBarHidden; +} + +- (void)onReset { + _eventsCallbackId = nil; +} + +- (void)fireTappedEvent { + if (_eventsCallbackId == nil) { + return; + } + NSDictionary* payload = @{@"type": @"tap"}; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:payload]; + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId]; +} + +- (void)updateIsVisible:(BOOL)visible { + if (_eventsCallbackId == nil) { + return; + } + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:visible]; + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId]; +} + +- (void) _ready:(CDVInvokedUrlCommand*)command +{ + _eventsCallbackId = command.callbackId; + [self updateIsVisible:![UIApplication sharedApplication].statusBarHidden]; + NSString* setting = @"StatusBarOverlaysWebView"; + if ([self settingForKey:setting]) { + self.statusBarOverlaysWebView = [(NSNumber*)[self settingForKey:setting] boolValue]; + if (self.statusBarOverlaysWebView) { + [self resizeWebView]; + } + } +} + +- (void) initializeStatusBarBackgroundView +{ + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + + if ([[UIApplication sharedApplication]statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown && + statusBarFrame.size.height + statusBarFrame.origin.y == [self.viewController.view.window bounds].size.height) { + + // When started in upside-down orientation on iOS 7, status bar will be bound to lower edge of the + // screen (statusBarFrame.origin.y will be somewhere around screen height). In this case we need to + // correct frame's coordinates + statusBarFrame.origin.y = 0; + } + + statusBarFrame = [self invertFrameIfNeeded:statusBarFrame]; + + _statusBarBackgroundView = [[UIView alloc] initWithFrame:statusBarFrame]; + _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor; + _statusBarBackgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin); + _statusBarBackgroundView.autoresizesSubviews = YES; +} + +- (CGRect) invertFrameIfNeeded:(CGRect)rect { + // landscape is where (width > height). On iOS < 8, we need to invert since frames are + // always in Portrait context. Do not run this on ios 8 or above to avoid breaking ipad pro multitask layout + if (!IsAtLeastiOSVersion(@"8.0") && UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { + CGFloat temp = rect.size.width; + rect.size.width = rect.size.height; + rect.size.height = temp; + rect.origin = CGPointZero; + } + + return rect; +} + +- (void) setStatusBarOverlaysWebView:(BOOL)statusBarOverlaysWebView +{ + // we only care about the latest iOS version or a change in setting + if (!IsAtLeastiOSVersion(@"7.0") || statusBarOverlaysWebView == _statusBarOverlaysWebView) { + return; + } + + _statusBarOverlaysWebView = statusBarOverlaysWebView; + + [self resizeWebView]; + + if (statusBarOverlaysWebView) { + + [_statusBarBackgroundView removeFromSuperview]; + + } else { + + [self initializeStatusBarBackgroundView]; + [self.webView.superview addSubview:_statusBarBackgroundView]; + + } + +} + +- (BOOL) statusBarOverlaysWebView +{ + return _statusBarOverlaysWebView; +} + +- (void) overlaysWebView:(CDVInvokedUrlCommand*)command +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSNumber class]])) { + value = [NSNumber numberWithBool:YES]; + } + + self.statusBarOverlaysWebView = [value boolValue]; +} + +- (void) refreshStatusBarAppearance +{ + SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate"); + if ([self.viewController respondsToSelector:sel]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [self.viewController performSelector:sel withObject:nil]; +#pragma clang diagnostic pop + } +} + +- (void) setStyleForStatusBar:(UIStatusBarStyle)style +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_statusBarStyle = [NSNumber numberWithInt:style]; + [self refreshStatusBarAppearance]; + + } else { + [[UIApplication sharedApplication] setStatusBarStyle:style]; + } +} + +- (void) setStatusBarStyle:(NSString*)statusBarStyle +{ + // default, lightContent, blackTranslucent, blackOpaque + NSString* lcStatusBarStyle = [statusBarStyle lowercaseString]; + + if ([lcStatusBarStyle isEqualToString:@"default"]) { + [self styleDefault:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"lightcontent"]) { + [self styleLightContent:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"blacktranslucent"]) { + [self styleBlackTranslucent:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"blackopaque"]) { + [self styleBlackOpaque:nil]; + } +} + +- (void) styleDefault:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleDefault]; +} + +- (void) styleLightContent:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleLightContent]; +} + +- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command +{ + #if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000 + # define TRANSLUCENT_STYLE UIStatusBarStyleBlackTranslucent + #else + # define TRANSLUCENT_STYLE UIStatusBarStyleLightContent + #endif + [self setStyleForStatusBar:TRANSLUCENT_STYLE]; +} + +- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command +{ + #if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000 + # define OPAQUE_STYLE UIStatusBarStyleBlackOpaque + #else + # define OPAQUE_STYLE UIStatusBarStyleLightContent + #endif + [self setStyleForStatusBar:OPAQUE_STYLE]; +} + +- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSString class]])) { + value = @"black"; + } + + SEL selector = NSSelectorFromString([value stringByAppendingString:@"Color"]); + if ([UIColor respondsToSelector:selector]) { + _statusBarBackgroundView.backgroundColor = [UIColor performSelector:selector]; + } +} + +- (void) _backgroundColorByHexString:(NSString*)hexString +{ + unsigned int rgbValue = 0; + NSScanner* scanner = [NSScanner scannerWithString:hexString]; + [scanner setScanLocation:1]; + [scanner scanHexInt:&rgbValue]; + + _statusBarBackgroundColor = [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; + _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor; +} + +- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command +{ + NSString* value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSString class]])) { + value = @"#000000"; + } + + if (![value hasPrefix:@"#"] || [value length] < 7) { + return; + } + + [self _backgroundColorByHexString:value]; +} + +- (void) hideStatusBar +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_hideStatusBar = [NSNumber numberWithBool:YES]; + [self refreshStatusBarAppearance]; + + } else { + UIApplication* app = [UIApplication sharedApplication]; + [app setStatusBarHidden:YES]; + } +} + +- (void) hide:(CDVInvokedUrlCommand*)command +{ + _statusBarVisible = NO; + UIApplication* app = [UIApplication sharedApplication]; + + if (!app.isStatusBarHidden) + { + + [self hideStatusBar]; + + if (IsAtLeastiOSVersion(@"7.0")) { + [_statusBarBackgroundView removeFromSuperview]; + } + + [self resizeWebView]; + + _statusBarBackgroundView.hidden = YES; + } +} + +- (void) showStatusBar +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_hideStatusBar = [NSNumber numberWithBool:NO]; + [self refreshStatusBarAppearance]; + + } else { + UIApplication* app = [UIApplication sharedApplication]; + [app setStatusBarHidden:NO]; + } +} + +- (void) show:(CDVInvokedUrlCommand*)command +{ + _statusBarVisible = YES; + UIApplication* app = [UIApplication sharedApplication]; + + if (app.isStatusBarHidden) + { + BOOL isIOS7 = (IsAtLeastiOSVersion(@"7.0")); + + [self showStatusBar]; + [self resizeWebView]; + + if (isIOS7) { + + if (!self.statusBarOverlaysWebView) { + + // there is a possibility that when the statusbar was hidden, it was in a different orientation + // from the current one. Therefore we need to expand the statusBarBackgroundView as well to the + // statusBar's current size + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + statusBarFrame = [self invertFrameIfNeeded:statusBarFrame]; + CGRect sbBgFrame = _statusBarBackgroundView.frame; + sbBgFrame.size = statusBarFrame.size; + _statusBarBackgroundView.frame = sbBgFrame; + [self.webView.superview addSubview:_statusBarBackgroundView]; + + } + + } + + _statusBarBackgroundView.hidden = NO; + } +} + +-(void)resizeWebView +{ + BOOL isIOS7 = (IsAtLeastiOSVersion(@"7.0")); + + if (isIOS7) { + CGRect bounds = [self.viewController.view.window bounds]; + if (CGRectEqualToRect(bounds, CGRectZero)) { + bounds = [[UIScreen mainScreen] bounds]; + } + bounds = [self invertFrameIfNeeded:bounds]; + + self.viewController.view.frame = bounds; + + self.webView.frame = bounds; + + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + statusBarFrame = [self invertFrameIfNeeded:statusBarFrame]; + CGRect frame = self.webView.frame; + CGFloat height = statusBarFrame.size.height; + + if (!self.statusBarOverlaysWebView) { + if (_statusBarVisible) { + // CB-10158 If a full screen video is playing the status bar height will be 0, set it to 20 if _statusBarVisible + frame.origin.y = height > 0 ? height: 20; + } + } else { + // Even if overlay is used, we want to handle in-call/recording/hotspot larger status bar + frame.origin.y = height >= 20 ? height - 20 : 0; + } + frame.size.height -= frame.origin.y; + self.webView.frame = frame; + } else { + CGRect bounds = [[UIScreen mainScreen] applicationFrame]; + self.viewController.view.frame = bounds; + } +} + +- (void) dealloc +{ + [[UIApplication sharedApplication] removeObserver:self forKeyPath:@"statusBarHidden"]; + [[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +} + + +#pragma mark - UIScrollViewDelegate + +- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView +{ + [self fireTappedEvent]; + return NO; +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h new file mode 100644 index 00000000..d7da331d --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h @@ -0,0 +1,5 @@ +#import <Foundation/Foundation.h> + +@interface NSString (URLEncoding) +@property (readonly) NSString *URLEncodedString; +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m new file mode 100644 index 00000000..b737626c --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m @@ -0,0 +1,30 @@ +#import "NSString+URLEncoding.h" + +@implementation NSString (URLEncoding) +- (NSString*)URLEncodedString +{ + NSString* result = (NSString *)CFBridgingRelease( + CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef)self, + CFSTR("#%"), // don't escape these + NULL, // allow escaping these + kCFStringEncodingUTF8 + ) + ); + + // we may have a URL with more than one '#' now - which iOS doesn't allow, so escape all but the first one + NSArray *parts = [result componentsSeparatedByString:@"#"]; + NSString *finalResult = parts[0]; + for (int i=1; i<parts.count; i++) { + NSString *part = [parts objectAtIndex:i]; + if (i==1) { + finalResult = [finalResult stringByAppendingString:@"#"]; + } else { + finalResult = [finalResult stringByAppendingString:@"%23"]; + } + finalResult = [finalResult stringByAppendingString:part]; + } + return finalResult; +} +@end
\ No newline at end of file diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h new file mode 100644 index 00000000..0c731450 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h @@ -0,0 +1,28 @@ +#import <Cordova/CDV.h> +#import <MessageUI/MFMailComposeViewController.h> + +@interface SocialSharing : CDVPlugin <UIPopoverControllerDelegate, MFMailComposeViewControllerDelegate, UIDocumentInteractionControllerDelegate> + +@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer; +@property (nonatomic, strong) UIDocumentInteractionController * documentInteractionController; +@property (retain) NSString * tempStoredFile; +@property (retain) CDVInvokedUrlCommand * command; + +- (void)available:(CDVInvokedUrlCommand*)command; +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command; +- (void)share:(CDVInvokedUrlCommand*)command; +- (void)shareWithOptions:(CDVInvokedUrlCommand*)command; +- (void)canShareVia:(CDVInvokedUrlCommand*)command; +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareVia:(CDVInvokedUrlCommand*)command; +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command; +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command; +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command; +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command; + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m new file mode 100644 index 00000000..014925bd --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m @@ -0,0 +1,772 @@ +#import "SocialSharing.h" +#import "NSString+URLEncoding.h" +#import <Cordova/CDV.h> +#import <Social/Social.h> +#import <Foundation/NSException.h> +#import <MessageUI/MFMessageComposeViewController.h> +#import <MessageUI/MFMailComposeViewController.h> +#import <MobileCoreServices/MobileCoreServices.h> + +static NSString *const kShareOptionMessage = @"message"; +static NSString *const kShareOptionSubject = @"subject"; +static NSString *const kShareOptionFiles = @"files"; +static NSString *const kShareOptionUrl = @"url"; + +@implementation SocialSharing { + UIPopoverController *_popover; + NSString *_popupCoordinates; +} + +- (void)pluginInitialize { + if ([self isEmailAvailable]) { + [self cycleTheGlobalMailComposer]; + } +} + +- (void)available:(CDVInvokedUrlCommand*)command { + BOOL avail = NO; + if (NSClassFromString(@"UIActivityViewController")) { + avail = YES; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSString*)getIPadPopupCoordinates { + if (_popupCoordinates != nil) { + return _popupCoordinates; + } + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + return [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.socialsharing.iPadPopupCoordinates();"]; + } else { + // prolly a wkwebview, ignoring for now + return nil; + } +} + +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command { + _popupCoordinates = [command.arguments objectAtIndex:0]; +} + +- (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps { + CGRect rect = CGRectZero; + if ([comps count] == 4) { + rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]); + } + return rect; +} + +- (void)share:(CDVInvokedUrlCommand*)command { + [self shareInternal:command + withOptions:@{ + kShareOptionMessage: [command.arguments objectAtIndex:0], + kShareOptionSubject: [command.arguments objectAtIndex:1], + kShareOptionFiles: [command.arguments objectAtIndex:2], + kShareOptionUrl: [command.arguments objectAtIndex:3] + } + isBooleanResponse:YES +]; +} + +- (void)shareWithOptions:(CDVInvokedUrlCommand*)command { + NSDictionary* options = [command.arguments objectAtIndex:0]; + [self shareInternal:command + withOptions:options + isBooleanResponse:NO + ]; +} + +- (void)shareInternal:(CDVInvokedUrlCommand*)command withOptions:(NSDictionary*)options isBooleanResponse:(BOOL)boolResponse { + [self.commandDelegate runInBackground:^{ //avoid main thread block especially if sharing big files from url + if (!NSClassFromString(@"UIActivityViewController")) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + NSString *message = options[kShareOptionMessage]; + NSString *subject = options[kShareOptionSubject]; + NSArray *filenames = options[kShareOptionFiles]; + NSString *urlString = options[kShareOptionUrl]; + + NSMutableArray *activityItems = [[NSMutableArray alloc] init]; + + if (message != (id)[NSNull null] && message != nil) { + [activityItems addObject:message]; + } + + if (filenames != (id)[NSNull null] && filenames != nil && filenames.count > 0) { + NSMutableArray *files = [[NSMutableArray alloc] init]; + for (NSString* filename in filenames) { + NSObject *file = [self getImage:filename]; + if (file == nil) { + file = [self getFile:filename]; + } + if (file != nil) { + [files addObject:file]; + } + } + [activityItems addObjectsFromArray:files]; + } + + if (urlString != (id)[NSNull null] && urlString != nil) { + [activityItems addObject:[NSURL URLWithString:[urlString URLEncodedString]]]; + } + + UIActivity *activity = [[UIActivity alloc] init]; + NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil]; + UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities]; + if (subject != (id)[NSNull null] && subject != nil) { + [activityVC setValue:subject forKey:@"subject"]; + } + + if ([activityVC respondsToSelector:(@selector(setCompletionWithItemsHandler:))]) { + [activityVC setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray * returnedItems, NSError * activityError) { + [self cleanupStoredFiles]; + if (boolResponse) { + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed] + callbackId:command.callbackId]; + } else { + NSDictionary * result = @{@"completed":@(completed), @"app":activityType == nil ? @"" : activityType}; + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result] + callbackId:command.callbackId]; + } + }]; + } else { + // let's suppress this warning otherwise folks will start opening issues while it's not relevant +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { + [self cleanupStoredFiles]; + NSDictionary * result = @{@"completed":@(completed), @"app":activityType}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + } + + NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"]; + if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) { + activityVC.excludedActivityTypes = socialSharingExcludeActivities; + } + + dispatch_async(dispatch_get_main_queue(), ^(void){ + // iPad on iOS >= 8 needs a different approach + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { + NSString* iPadCoords = [self getIPadPopupCoordinates]; + if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) { + NSArray *comps = [iPadCoords componentsSeparatedByString:@","]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } else { + _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC]; + _popover.delegate = self; + [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; + } + } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8) + NSArray *comps = [NSArray arrayWithObjects: + [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200], + [NSNumber numberWithInt:self.viewController.view.frame.size.height], + [NSNumber numberWithInt:400], + [NSNumber numberWithInt:400], + nil]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } + } + [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil]; + }); + }]; +} + +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeTwitter]; +} + +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command { + // If Fb app is installed a message is not prefilled. + // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already. + NSString *message = [command.arguments objectAtIndex:0]; + if (message != (id)[NSNull null]) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9 + if (fbAppInstalled) { + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + NSString *hint = [command.arguments objectAtIndex:4]; + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]; + [alert show]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [alert dismissWithClickedButtonIndex:-1 animated:YES]; + }); + } + }); + } + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareVia:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:[command.arguments objectAtIndex:4]]; +} + +- (void)canShareVia:(CDVInvokedUrlCommand*)command { + NSString *via = [command.arguments objectAtIndex:4]; + CDVPluginResult * pluginResult; + if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([self isAvailableForSharing:command type:via]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (bool)isEmailAvailable { + Class messageClass = (NSClassFromString(@"MFMailComposeViewController")); + return messageClass != nil && [messageClass canSendMail]; +} + +- (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + // isAvailableForServiceType returns true if you pass it a type that is not + // in the defined constants, this is probably a bug on apples part + if(!([type isEqualToString:SLServiceTypeFacebook] + || [type isEqualToString:SLServiceTypeTwitter] + || [type isEqualToString:SLServiceTypeTencentWeibo] + || [type isEqualToString:SLServiceTypeSinaWeibo])) { + return false; + } + // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed + @try { + return [SLComposeViewController isAvailableForServiceType:type]; + } + @catch (NSException* exception) { + return false; + } +} + +- (void)shareViaInternal:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + // boldly invoke the target app, because the phone will display a nice message asking to configure the app + SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type]; + if (message != (id)[NSNull null]) { + [composeViewController setInitialText:message]; + } + + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + [composeViewController addImage:image]; + } + } + + if (urlString != (id)[NSNull null]) { + [composeViewController addURL:[NSURL URLWithString:[urlString URLEncodedString]]]; + } + + [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) { + if (SLComposeViewControllerResultCancelled == result) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else if ([self isAvailableForSharing:command type:type]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:SLComposeViewControllerResultDone == result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + // required for iOS6 (issues #162 and #167) + [self.viewController dismissViewControllerAnimated:YES completion:nil]; + }]; + [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil]; +} + +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + + if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin" + message:@"Sharing via email is not supported on the iOS 8 simulator." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alert show]; + return; + } + + [self cycleTheGlobalMailComposer]; + + self.globalMailComposer.mailComposeDelegate = self; + + if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) { + NSString *message = [command.arguments objectAtIndex:0]; + BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound; + [self.globalMailComposer setMessageBody:message isHTML:isHTML]; + } + + if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) { + [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]]; + } + + if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) { + [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]]; + } + + if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) { + [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]]; + } + + if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) { + [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]]; + } + + if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) { + NSArray* attachments = [command.arguments objectAtIndex:5]; + NSFileManager* fileManager = [NSFileManager defaultManager]; + for (NSString* path in attachments) { + NSURL *file = [self getFile:path]; + NSData* data = [fileManager contentsAtPath:file.path]; + + NSString* fileName; + NSString* mimeType; + NSString* basename = [self getBasenameFromAttachmentPath:path]; + + if ([basename hasPrefix:@"data:"]) { + mimeType = (NSString*)[[[basename substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileName = @"attachment."; + fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]]; + NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject]; + data = [SocialSharing dataFromBase64String:base64content]; + } else { + fileName = [basename pathComponents].lastObject; + mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]]; + } + [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName]; + } + } + + // remember the command, because we need it in the didFinishWithResult method + _command = command; + + [self.commandDelegate runInBackground:^{ + [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil]; + }]; + + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (UIViewController*) getTopMostViewController { + UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController; + while (presentingViewController.presentedViewController != nil) { + presentingViewController = presentingViewController.presentedViewController; + } + return presentingViewController; +} + +- (NSString*) getBasenameFromAttachmentPath:(NSString*)path { + if ([path hasPrefix:@"base64:"]) { + NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""]; + return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location]; + } + return [path componentsSeparatedByString: @"?"][0]; +} + +- (NSString*) getMimeTypeFromFileExtension:(NSString*)extension { + if (!extension) { + return nil; + } + // Get the UTI from the file's extension + CFStringRef ext = (CFStringRef)CFBridgingRetain(extension); + CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL); + // Converting UTI to a mime type + NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType)); + CFRelease(ext); + CFRelease(type); + return result; +} + +/** + * Delegate will be called after the mail composer did finish an action + * to dismiss the view. + */ +- (void) mailComposeController:(MFMailComposeViewController*)controller + didFinishWithResult:(MFMailComposeResult)result + error:(NSError*)error { + bool ok = result == MFMailComposeResultSent; + [self.globalMailComposer dismissViewControllerAnimated:YES completion:^{[self cycleTheGlobalMailComposer];}]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +-(void)cycleTheGlobalMailComposer { + // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976 + self.globalMailComposer = nil; + self.globalMailComposer = [[MFMailComposeViewController alloc] init]; +} + +- (bool)canShareViaSMS { + Class messageClass = (NSClassFromString(@"MFMessageComposeViewController")); + return messageClass != nil && [messageClass canSendText]; +} + +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command { + if ([self canShareViaSMS]) { + NSDictionary* options = [command.arguments objectAtIndex:0]; + NSString *phonenumbers = [command.arguments objectAtIndex:1]; + NSString *message = [options objectForKey:@"message"]; + NSString *subject = [options objectForKey:@"subject"]; + NSString *image = [options objectForKey:@"image"]; + + MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init]; + picker.messageComposeDelegate = (id) self; + if (message != (id)[NSNull null]) { + picker.body = message; + } + if (subject != (id)[NSNull null]) { + [picker setSubject:subject]; + } + if (image != nil && image != (id)[NSNull null]) { + BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)]; + if (canSendAttachments) { + NSURL *file = [self getFile:image]; + if (file != nil) { + [picker addAttachmentURL:file withAlternateFilename:nil]; + } + } + } + + if (phonenumbers != (id)[NSNull null]) { + [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]]; + } + // remember the command, because we need it in the didFinishWithResult method + _command = command; + [self.commandDelegate runInBackground:^{ + picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + [[self getTopMostViewController] presentViewController:picker animated:YES completion:nil]; + }]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +// Dismisses the SMS composition interface when users taps Cancel or Send +- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result { + bool ok = result == MessageComposeResultSent; + [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +- (bool)canShareViaInstagram { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9 +} + +- (bool)canShareViaWhatsApp { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9 +} + +// 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, .. +- (void)openImage:(NSString *)imageName { + UIImage* image =[self getImage:imageName]; + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } +} + +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than Instagram sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaInstagram]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + +// NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0]; + NSString *tmpDir = NSTemporaryDirectory(); + NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES]; + + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]]; + _documentInteractionController.delegate = self; + _documentInteractionController.UTI = @"com.instagram.exclusivegram"; + + if (message != (id)[NSNull null]) { + // no longer working, so .. + _documentInteractionController.annotation = @{@"InstagramCaption" : message}; + + // .. we put the message on the clipboard (you app can prompt the user to paste it) + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + } + + // remember the command for the delegate method + _command = command; + + // test for #513 + dispatch_async(dispatch_get_main_queue(), ^(void){ + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES]; + }); +} + +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaWhatsApp]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + NSString *abid = [command.arguments objectAtIndex:4]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + + // with WhatsApp, we can share an image OR text+url.. image wins if set + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.wai"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @"net.whatsapp.image"; + _documentInteractionController.delegate = self; + _command = command; + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } else { + // append an url to a message, if both are passed + NSString * shareString = @""; + if (message != (id)[NSNull null]) { + shareString = message; + } + if (urlString != (id)[NSNull null]) { + if ([shareString isEqual: @""]) { + shareString = urlString; + } else { + shareString = [NSString stringWithFormat:@"%@ %@", shareString, [urlString URLEncodedString]]; + } + } + NSString * encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + // also encode the '=' character + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"]; + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; + NSString * abidString = @""; + if (abid != (id)[NSNull null]) { + abidString = [NSString stringWithFormat:@"abid=%@&", abid]; + } + NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?%@text=%@", abidString, encodedShareString]; + + NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp]; + [[UIApplication sharedApplication] openURL: whatsappURL]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command { + self.command = command; + NSArray *filenames = [command.arguments objectAtIndex:0]; + [self.commandDelegate runInBackground:^{ + bool shared = false; + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + shared = true; + UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil); + } + } + if (!shared) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } + }]; +} + +// called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback) +// but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem +- (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo { + if (error) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } +} + +-(UIImage*)getImage: (NSString *)imageName { + UIImage *image = nil; + if (imageName != (id)[NSNull null]) { + if ([imageName hasPrefix:@"http"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]]; + } else if ([imageName hasPrefix:@"www/"]) { + image = [UIImage imageNamed:imageName]; + } else if ([imageName hasPrefix:@"file://"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]]; + } else if ([imageName hasPrefix:@"data:"]) { + // using a base64 encoded string + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else if ([imageName hasPrefix:@"assets-library://"]) { + // use assets-library + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else { + // assume anywhere else, on the local filesystem + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]]; + } + } + return image; +} + +-(NSURL*)getFile: (NSString *)fileName { + NSURL *file = nil; + if (fileName != (id)[NSNull null]) { + if ([fileName hasPrefix:@"http"]) { + NSURL *url = [NSURL URLWithString:fileName]; + NSData *fileData = [NSData dataWithContentsOfURL:url]; + NSString *name = (NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject]; + file = [NSURL fileURLWithPath:[self storeInFile:[name componentsSeparatedByString: @"?"][0] fileData:fileData]]; + } else if ([fileName hasPrefix:@"www/"]) { + NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; + NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName]; + file = [NSURL fileURLWithPath:fullPath]; + } else if ([fileName hasPrefix:@"file://"]) { + // stripping the first 6 chars, because the path should start with / instead of file:// + file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]]; + } else if ([fileName hasPrefix:@"data:"]) { + // using a base64 encoded string + // extract some info from the 'fileName', which is for example: data:text/calendar;base64,<encoded stuff here> + NSString *fileType = (NSString*)[[[fileName substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileType = (NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject]; + NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject]; + NSData *fileData = [SocialSharing dataFromBase64String:base64content]; + file = [NSURL fileURLWithPath:[self storeInFile:[NSString stringWithFormat:@"%@.%@", @"file", fileType] fileData:fileData]]; + } else { + // assume anywhere else, on the local filesystem + file = [NSURL fileURLWithPath:fileName]; + } + } + return file; +} + +-(NSString*) storeInFile: (NSString*) fileName + fileData: (NSData*) fileData { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName]; + [fileData writeToFile:filePath atomically:YES]; + _tempStoredFile = filePath; + return filePath; +} + +- (void) cleanupStoredFiles { + if (_tempStoredFile != nil) { + NSError *error; + [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error]; + } +} + ++ (NSData*) dataFromBase64String:(NSString*)aString { + return [[NSData alloc] initWithBase64EncodedString:aString options:0]; +} + +#pragma mark - UIPopoverControllerDelegate methods + +- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view { + NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","]; + CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps]; + rect->origin = newRect.origin; +} + +- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { + _popover = nil; +} + +#pragma mark - UIDocumentInteractionControllerDelegate methods + +- (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application { + // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only) + NSLog(@"SocialSharing app selected: %@", application); +} + +- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller { + if (self.command != nil) { + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId]; + } +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h new file mode 100644 index 00000000..63935dc3 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h @@ -0,0 +1,16 @@ +#import <Cordova/CDVPlugin.h> +#import <objc/runtime.h> + +@interface IonicKeyboard : CDVPlugin <UIScrollViewDelegate> { + @protected + id _keyboardShowObserver, _keyboardHideObserver; + IMP wkOriginalImp, uiOriginalImp, nilImp; + Method wkMethod, uiMethod; +} + +@property (readwrite, assign) BOOL hideKeyboardAccessoryBar; +@property (readwrite, assign) BOOL disableScroll; +//@property (readwrite, assign) BOOL styleDark; + +@end + diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m new file mode 100644 index 00000000..d072ca7f --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m @@ -0,0 +1,176 @@ +#import "IonicKeyboard.h" +// #import "UIWebViewExtension.h" +#import <Cordova/CDVAvailability.h> + +@implementation IonicKeyboard + +@synthesize hideKeyboardAccessoryBar = _hideKeyboardAccessoryBar; +@synthesize disableScroll = _disableScroll; +//@synthesize styleDark = _styleDark; + +- (void)pluginInitialize { + + Class wkClass = NSClassFromString([@[@"UI", @"Web", @"Browser", @"View"] componentsJoinedByString:@""]); + wkMethod = class_getInstanceMethod(wkClass, @selector(inputAccessoryView)); + wkOriginalImp = method_getImplementation(wkMethod); + Class uiClass = NSClassFromString([@[@"WK", @"Content", @"View"] componentsJoinedByString:@""]); + uiMethod = class_getInstanceMethod(uiClass, @selector(inputAccessoryView)); + uiOriginalImp = method_getImplementation(uiMethod); + nilImp = imp_implementationWithBlock(^(id _s) { + return nil; + }); + + //set defaults + self.hideKeyboardAccessoryBar = YES; + self.disableScroll = NO; + //self.styleDark = NO; + + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + __weak IonicKeyboard* weakSelf = self; + _keyboardShowObserver = [nc addObserverForName:UIKeyboardWillShowNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* notification) { + + CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; + keyboardFrame = [self.viewController.view convertRect:keyboardFrame fromView:nil]; + + [weakSelf.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.plugins.Keyboard.isVisible = true; cordova.fireWindowEvent('native.keyboardshow', { 'keyboardHeight': %@ }); ", [@(keyboardFrame.size.height) stringValue]]]; + + //deprecated + [weakSelf.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireWindowEvent('native.showkeyboard', { 'keyboardHeight': %@ }); ", [@(keyboardFrame.size.height) stringValue]]]; + }]; + + _keyboardHideObserver = [nc addObserverForName:UIKeyboardWillHideNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* notification) { + [weakSelf.commandDelegate evalJs:@"cordova.plugins.Keyboard.isVisible = false; cordova.fireWindowEvent('native.keyboardhide'); "]; + + //deprecated + [weakSelf.commandDelegate evalJs:@"cordova.fireWindowEvent('native.hidekeyboard'); "]; + }]; +} + +- (BOOL)disableScroll { + return _disableScroll; +} + +- (void)setDisableScroll:(BOOL)disableScroll { + if (disableScroll == _disableScroll) { + return; + } + if (disableScroll) { + self.webView.scrollView.scrollEnabled = NO; + self.webView.scrollView.delegate = self; + } + else { + self.webView.scrollView.scrollEnabled = YES; + self.webView.scrollView.delegate = nil; + } + + _disableScroll = disableScroll; +} + +//keyboard swizzling inspired by: +//https://github.com/cjpearson/cordova-plugin-keyboard/ + +- (BOOL)hideKeyboardAccessoryBar { + return _hideKeyboardAccessoryBar; +} + +- (void)setHideKeyboardAccessoryBar:(BOOL)hideKeyboardAccessoryBar { + if (hideKeyboardAccessoryBar == _hideKeyboardAccessoryBar) { + return; + } + + if (hideKeyboardAccessoryBar) { + method_setImplementation(wkMethod, nilImp); + method_setImplementation(uiMethod, nilImp); + } else { + method_setImplementation(wkMethod, wkOriginalImp); + method_setImplementation(uiMethod, uiOriginalImp); + } + + _hideKeyboardAccessoryBar = hideKeyboardAccessoryBar; +} + +/* +- (BOOL)styleDark { + return _styleDark; +} + +- (void)setStyleDark:(BOOL)styleDark { + if (styleDark == _styleDark) { + return; + } + if (styleDark) { + self.webView.styleDark = YES; + } + else { + self.webView.styleDark = NO; + } + + _styleDark = styleDark; +} +*/ + + +/* ------------------------------------------------------------- */ + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { + [scrollView setContentOffset: CGPointZero]; +} + +/* ------------------------------------------------------------- */ + +- (void)dealloc { + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + + [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil]; + [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil]; +} + +/* ------------------------------------------------------------- */ + +- (void) disableScroll:(CDVInvokedUrlCommand*)command { + if (!command.arguments || ![command.arguments count]){ + return; + } + id value = [command.arguments objectAtIndex:0]; + if (value != [NSNull null]) { + self.disableScroll = [value boolValue]; + } +} + +- (void) hideKeyboardAccessoryBar:(CDVInvokedUrlCommand*)command { + if (!command.arguments || ![command.arguments count]){ + return; + } + id value = [command.arguments objectAtIndex:0]; + if (value != [NSNull null]) { + self.hideKeyboardAccessoryBar = [value boolValue]; + } +} + +- (void) close:(CDVInvokedUrlCommand*)command { + [self.webView endEditing:YES]; +} + +- (void) show:(CDVInvokedUrlCommand*)command { + NSLog(@"Showing keyboard not supported in iOS due to platform limitations."); +} + +/* +- (void) styleDark:(CDVInvokedUrlCommand*)command { + if (!command.arguments || ![command.arguments count]){ + return; + } + id value = [command.arguments objectAtIndex:0]; + + self.styleDark = [value boolValue]; +} +*/ + +@end + diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h new file mode 100644 index 00000000..9970762b --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h @@ -0,0 +1,22 @@ +// +// AppDelegate+notification.h +// pushtest +// +// Created by Robert Easterday on 10/26/12. +// +// + +#import "AppDelegate.h" + +@interface AppDelegate (notification) +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:( void (^)(UIBackgroundFetchResult))completionHandler; +- (void)pushPluginOnApplicationDidBecomeActive:(UIApplication *)application; +- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler; +- (id) getCommandInstance:(NSString*)className; + +@property (nonatomic, retain) NSDictionary *launchNotification; +@property (nonatomic, retain) NSNumber *coldstart; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m new file mode 100644 index 00000000..fc18dd79 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m @@ -0,0 +1,278 @@ +// +// AppDelegate+notification.m +// pushtest +// +// Created by Robert Easterday on 10/26/12. +// +// + +#import "AppDelegate+notification.h" +#import "PushPlugin.h" +#import <objc/runtime.h> + +static char launchNotificationKey; +static char coldstartKey; + +@implementation AppDelegate (notification) + +- (id) getCommandInstance:(NSString*)className +{ + return [self.viewController getCommandInstance:className]; +} + +// its dangerous to override a method from within a category. +// Instead we will use method swizzling. we set this up in the load call. ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class class = [self class]; + + SEL originalSelector = @selector(init); + SEL swizzledSelector = @selector(pushPluginSwizzledInit); + + Method original = class_getInstanceMethod(class, originalSelector); + Method swizzled = class_getInstanceMethod(class, swizzledSelector); + + BOOL didAddMethod = + class_addMethod(class, + originalSelector, + method_getImplementation(swizzled), + method_getTypeEncoding(swizzled)); + + if (didAddMethod) { + class_replaceMethod(class, + swizzledSelector, + method_getImplementation(original), + method_getTypeEncoding(original)); + } else { + method_exchangeImplementations(original, swizzled); + } + }); +} + +- (AppDelegate *)pushPluginSwizzledInit +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(createNotificationChecker:) + name:UIApplicationDidFinishLaunchingNotification + object:nil]; + [[NSNotificationCenter defaultCenter]addObserver:self + selector:@selector(pushPluginOnApplicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + // This actually calls the original init method over in AppDelegate. Equivilent to calling super + // on an overrided method, this is not recursive, although it appears that way. neat huh? + return [self pushPluginSwizzledInit]; +} + +// This code will be called immediately after application:didFinishLaunchingWithOptions:. We need +// to process notifications in cold-start situations +- (void)createNotificationChecker:(NSNotification *)notification +{ + NSLog(@"createNotificationChecker"); + if (notification) + { + NSDictionary *launchOptions = [notification userInfo]; + if (launchOptions) { + NSLog(@"coldstart"); + self.launchNotification = [launchOptions objectForKey: @"UIApplicationLaunchOptionsRemoteNotificationKey"]; + self.coldstart = [NSNumber numberWithBool:YES]; + } else { + NSLog(@"not coldstart"); + self.coldstart = [NSNumber numberWithBool:NO]; + } + } +} + +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + [pushHandler didFailToRegisterForRemoteNotificationsWithError:error]; +} + +- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSLog(@"clicked on the shade"); + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = NO; + [pushHandler notificationReceived]; +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + NSLog(@"didReceiveNotification with fetchCompletionHandler"); + + // app is in the foreground so call notification callback + if (application.applicationState == UIApplicationStateActive) { + NSLog(@"app active"); + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = YES; + [pushHandler notificationReceived]; + + completionHandler(UIBackgroundFetchResultNewData); + } + // app is in background or in stand by + else { + NSLog(@"app in-active"); + + // do some convoluted logic to find out if this should be a silent push. + long silent = 0; + id aps = [userInfo objectForKey:@"aps"]; + id contentAvailable = [aps objectForKey:@"content-available"]; + if ([contentAvailable isKindOfClass:[NSString class]] && [contentAvailable isEqualToString:@"1"]) { + silent = 1; + } else if ([contentAvailable isKindOfClass:[NSNumber class]]) { + silent = [contentAvailable integerValue]; + } + + if (silent == 1) { + NSLog(@"this should be a silent push"); + void (^safeHandler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result){ + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(result); + }); + }; + + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + + if (pushHandler.handlerObj == nil) { + pushHandler.handlerObj = [NSMutableDictionary dictionaryWithCapacity:2]; + } + + id notId = [userInfo objectForKey:@"notId"]; + if (notId != nil) { + NSLog(@"Push Plugin notId %@", notId); + [pushHandler.handlerObj setObject:safeHandler forKey:notId]; + } else { + NSLog(@"Push Plugin notId handler"); + [pushHandler.handlerObj setObject:safeHandler forKey:@"handler"]; + } + + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = NO; + [pushHandler notificationReceived]; + } else { + NSLog(@"just put it in the shade"); + //save it for later + self.launchNotification = userInfo; + + completionHandler(UIBackgroundFetchResultNewData); + } + } +} + +- (BOOL)userHasRemoteNotificationsEnabled { + UIApplication *application = [UIApplication sharedApplication]; + if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) { + return application.currentUserNotificationSettings.types != UIUserNotificationTypeNone; + } else { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return application.enabledRemoteNotificationTypes != UIRemoteNotificationTypeNone; +#pragma GCC diagnostic pop + } +} + +- (void)pushPluginOnApplicationDidBecomeActive:(NSNotification *)notification { + + NSLog(@"active"); + + UIApplication *application = notification.object; + + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + if (pushHandler.clearBadge) { + NSLog(@"PushPlugin clearing badge"); + //zero badge + application.applicationIconBadgeNumber = 0; + } else { + NSLog(@"PushPlugin skip clear badge"); + } + + if (self.launchNotification) { + pushHandler.isInline = NO; + pushHandler.coldstart = [self.coldstart boolValue]; + pushHandler.notificationMessage = self.launchNotification; + self.launchNotification = nil; + self.coldstart = [NSNumber numberWithBool:NO]; + [pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO]; + } +} + + +- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier +forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler { + + NSLog(@"Push Plugin handleActionWithIdentifier %@", identifier); + NSMutableDictionary *userInfo = [notification mutableCopy]; + [userInfo setObject:identifier forKey:@"actionCallback"]; + NSLog(@"Push Plugin userInfo %@", userInfo); + + if (application.applicationState == UIApplicationStateActive) { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = NO; + [pushHandler notificationReceived]; + } else { + void (^safeHandler)() = ^(void){ + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(); + }); + }; + + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + + if (pushHandler.handlerObj == nil) { + pushHandler.handlerObj = [NSMutableDictionary dictionaryWithCapacity:2]; + } + + id notId = [userInfo objectForKey:@"notId"]; + if (notId != nil) { + NSLog(@"Push Plugin notId %@", notId); + [pushHandler.handlerObj setObject:safeHandler forKey:notId]; + } else { + NSLog(@"Push Plugin notId handler"); + [pushHandler.handlerObj setObject:safeHandler forKey:@"handler"]; + } + + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = NO; + + [pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO]; + } +} + +// The accessors use an Associative Reference since you can't define a iVar in a category +// http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html +- (NSMutableArray *)launchNotification +{ + return objc_getAssociatedObject(self, &launchNotificationKey); +} + +- (void)setLaunchNotification:(NSDictionary *)aDictionary +{ + objc_setAssociatedObject(self, &launchNotificationKey, aDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSNumber *)coldstart +{ + return objc_getAssociatedObject(self, &coldstartKey); +} + +- (void)setColdstart:(NSNumber *)aNumber +{ + objc_setAssociatedObject(self, &coldstartKey, aNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)dealloc +{ + self.launchNotification = nil; // clear the association and release the object + self.coldstart = nil; +} + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h new file mode 100644 index 00000000..4cc1dcb0 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h @@ -0,0 +1,79 @@ +/* + Copyright 2009-2011 Urban Airship Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binaryform must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided withthe distribution. + + THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> +#import <Cordova/CDV.h> +#import <Cordova/CDVPlugin.h> + +@protocol GGLInstanceIDDelegate; +@protocol GCMReceiverDelegate; +@interface PushPlugin : CDVPlugin +{ + NSDictionary *notificationMessage; + BOOL isInline; + NSString *notificationCallbackId; + NSString *callback; + BOOL clearBadge; + + NSMutableDictionary *handlerObj; + void (^completionHandler)(UIBackgroundFetchResult); + + BOOL ready; +} + +@property (nonatomic, copy) NSString *callbackId; +@property (nonatomic, copy) NSString *notificationCallbackId; +@property (nonatomic, copy) NSString *callback; + +@property (nonatomic, strong) NSDictionary *notificationMessage; +@property BOOL isInline; +@property BOOL coldstart; +@property BOOL clearBadge; +@property (nonatomic, strong) NSMutableDictionary *handlerObj; + +- (void)init:(CDVInvokedUrlCommand*)command; +- (void)unregister:(CDVInvokedUrlCommand*)command; +- (void)subscribe:(CDVInvokedUrlCommand*)command; +- (void)unsubscribe:(CDVInvokedUrlCommand*)command; + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; + +- (void)setNotificationMessage:(NSDictionary *)notification; +- (void)notificationReceived; + +- (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error; +- (void)didSendDataMessageWithID:(NSString *)messageID; +- (void)didDeleteMessagesOnServer; + +// FCM Features +@property(nonatomic, assign) BOOL usesFCM; +@property(nonatomic, strong) NSNumber *fcmSandbox; +@property(nonatomic, strong) NSString *fcmSenderId; +@property(nonatomic, strong) NSDictionary *fcmRegistrationOptions; +@property(nonatomic, strong) NSString *fcmRegistrationToken; +@property(nonatomic, strong) NSArray *fcmTopics; + +@end diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m new file mode 100644 index 00000000..90475d10 --- /dev/null +++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m @@ -0,0 +1,608 @@ +/* + Copyright 2009-2011 Urban Airship Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binaryform must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided withthe distribution. + + THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// See GGLInstanceID.h +#define GMP_NO_MODULES true + +#import "PushPlugin.h" +@import FirebaseInstanceID; +@import FirebaseMessaging; +@import FirebaseAnalytics; + +@implementation PushPlugin : CDVPlugin + +@synthesize notificationMessage; +@synthesize isInline; +@synthesize coldstart; + +@synthesize callbackId; +@synthesize notificationCallbackId; +@synthesize callback; +@synthesize clearBadge; +@synthesize handlerObj; + +@synthesize usesFCM; +@synthesize fcmSandbox; +@synthesize fcmSenderId; +@synthesize fcmRegistrationOptions; +@synthesize fcmRegistrationToken; +@synthesize fcmTopics; + +-(void)initRegistration; +{ + NSString * registrationToken = [[FIRInstanceID instanceID] token]; + + if (registrationToken != nil) { + NSLog(@"FCM Registration Token: %@", registrationToken); + [self setFcmRegistrationToken: registrationToken]; + + id topics = [self fcmTopics]; + if (topics != nil) { + for (NSString *topic in topics) { + NSLog(@"subscribe to topic: %@", topic); + id pubSub = [FIRMessaging messaging]; + [pubSub subscribeToTopic:topic]; + } + } + + [self registerWithToken:registrationToken]; + } else { + NSLog(@"FCM token is null"); + } + +} + +// FCM refresh token +// Unclear how this is testable under normal circumstances +- (void)onTokenRefresh { +#if !TARGET_IPHONE_SIMULATOR + // A rotation of the registration tokens is happening, so the app needs to request a new token. + NSLog(@"The FCM registration token needs to be changed."); + [[FIRInstanceID instanceID] token]; + [self initRegistration]; +#endif +} + +// contains error info +- (void)sendDataMessageFailure:(NSNotification *)notification { + NSLog(@"sendDataMessageFailure"); +} +- (void)sendDataMessageSuccess:(NSNotification *)notification { + NSLog(@"sendDataMessageSuccess"); +} + +- (void)didSendDataMessageWithID:messageID { + NSLog(@"didSendDataMessageWithID"); +} + +- (void)willSendDataMessageWithID:messageID error:error { + NSLog(@"willSendDataMessageWithID"); +} + +- (void)didDeleteMessagesOnServer { + NSLog(@"didDeleteMessagesOnServer"); + // Some messages sent to this device were deleted on the GCM server before reception, likely + // because the TTL expired. The client should notify the app server of this, so that the app + // server can resend those messages. +} + +- (void)unregister:(CDVInvokedUrlCommand*)command; +{ + NSArray* topics = [command argumentAtIndex:0]; + + if (topics != nil) { + id pubSub = [FIRMessaging messaging]; + for (NSString *topic in topics) { + NSLog(@"unsubscribe from topic: %@", topic); + [pubSub unsubscribeFromTopic:topic]; + } + } else { + [[UIApplication sharedApplication] unregisterForRemoteNotifications]; + [self successWithMessage:command.callbackId withMsg:@"unregistered"]; + } +} + +- (void)subscribe:(CDVInvokedUrlCommand*)command; +{ + NSString* topic = [command argumentAtIndex:0]; + + if (topic != nil) { + NSLog(@"subscribe from topic: %@", topic); + id pubSub = [FIRMessaging messaging]; + [pubSub subscribeToTopic:topic]; + NSLog(@"Successfully subscribe to topic %@", topic); + [self successWithMessage:command.callbackId withMsg:[NSString stringWithFormat:@"Successfully subscribe to topic %@", topic]]; + } else { + NSLog(@"There is no topic to subscribe"); + [self successWithMessage:command.callbackId withMsg:@"There is no topic to subscribe"]; + } +} + +- (void)unsubscribe:(CDVInvokedUrlCommand*)command; +{ + NSString* topic = [command argumentAtIndex:0]; + + if (topic != nil) { + NSLog(@"unsubscribe from topic: %@", topic); + id pubSub = [FIRMessaging messaging]; + [pubSub unsubscribeFromTopic:topic]; + NSLog(@"Successfully unsubscribe from topic %@", topic); + [self successWithMessage:command.callbackId withMsg:[NSString stringWithFormat:@"Successfully unsubscribe from topic %@", topic]]; + } else { + NSLog(@"There is no topic to unsubscribe"); + [self successWithMessage:command.callbackId withMsg:@"There is no topic to unsubscribe"]; + } +} + +- (void)init:(CDVInvokedUrlCommand*)command; +{ + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(onTokenRefresh) + name:kFIRInstanceIDTokenRefreshNotification object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(sendDataMessageFailure:) + name:FIRMessagingSendErrorNotification object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(sendDataMessageSuccess:) + name:FIRMessagingSendSuccessNotification object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(didDeleteMessagesOnServer) + name:FIRMessagingMessagesDeletedNotification object:nil]; + + [self.commandDelegate runInBackground:^ { + + NSLog(@"Push Plugin register called"); + self.callbackId = command.callbackId; + + NSMutableDictionary* options = [command.arguments objectAtIndex:0]; + NSMutableDictionary* iosOptions = [options objectForKey:@"ios"]; + + NSArray* topics = [iosOptions objectForKey:@"topics"]; + [self setFcmTopics:topics]; + + UIUserNotificationType UserNotificationTypes = UIUserNotificationTypeNone; + + id badgeArg = [iosOptions objectForKey:@"badge"]; + id soundArg = [iosOptions objectForKey:@"sound"]; + id alertArg = [iosOptions objectForKey:@"alert"]; + id clearBadgeArg = [iosOptions objectForKey:@"clearBadge"]; + + if (([badgeArg isKindOfClass:[NSString class]] && [badgeArg isEqualToString:@"true"]) || [badgeArg boolValue]) + { + UserNotificationTypes |= UIUserNotificationTypeBadge; + } + + if (([soundArg isKindOfClass:[NSString class]] && [soundArg isEqualToString:@"true"]) || [soundArg boolValue]) + { + UserNotificationTypes |= UIUserNotificationTypeSound; + } + + if (([alertArg isKindOfClass:[NSString class]] && [alertArg isEqualToString:@"true"]) || [alertArg boolValue]) + { + UserNotificationTypes |= UIUserNotificationTypeAlert; + } + + UserNotificationTypes |= UIUserNotificationActivationModeBackground; + + if (clearBadgeArg == nil || ([clearBadgeArg isKindOfClass:[NSString class]] && [clearBadgeArg isEqualToString:@"false"]) || ![clearBadgeArg boolValue]) { + NSLog(@"PushPlugin.register: setting badge to false"); + clearBadge = NO; + } else { + NSLog(@"PushPlugin.register: setting badge to true"); + clearBadge = YES; + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; + } + NSLog(@"PushPlugin.register: clear badge is set to %d", clearBadge); + + isInline = NO; + + NSLog(@"PushPlugin.register: better button setup"); + // setup action buttons + NSMutableSet *categories = [[NSMutableSet alloc] init]; + id categoryOptions = [iosOptions objectForKey:@"categories"]; + if (categoryOptions != nil && [categoryOptions isKindOfClass:[NSDictionary class]]) { + for (id key in categoryOptions) { + NSLog(@"categories: key %@", key); + id category = [categoryOptions objectForKey:key]; + + id yesButton = [category objectForKey:@"yes"]; + UIMutableUserNotificationAction *yesAction; + if (yesButton != nil && [yesButton isKindOfClass:[NSDictionary class]]) { + yesAction = [self createAction: yesButton]; + } + id noButton = [category objectForKey:@"no"]; + UIMutableUserNotificationAction *noAction; + if (noButton != nil && [noButton isKindOfClass:[NSDictionary class]]) { + noAction = [self createAction: noButton]; + } + id maybeButton = [category objectForKey:@"maybe"]; + UIMutableUserNotificationAction *maybeAction; + if (maybeButton != nil && [maybeButton isKindOfClass:[NSDictionary class]]) { + maybeAction = [self createAction: maybeButton]; + } + + // First create the category + UIMutableUserNotificationCategory *notificationCategory = [[UIMutableUserNotificationCategory alloc] init]; + + // Identifier to include in your push payload and local notification + notificationCategory.identifier = key; + + NSMutableArray *categoryArray = [[NSMutableArray alloc] init]; + NSMutableArray *minimalCategoryArray = [[NSMutableArray alloc] init]; + if (yesButton != nil) { + [categoryArray addObject:yesAction]; + [minimalCategoryArray addObject:yesAction]; + } + if (noButton != nil) { + [categoryArray addObject:noAction]; + [minimalCategoryArray addObject:noAction]; + } + if (maybeButton != nil) { + [categoryArray addObject:maybeAction]; + } + + // Add the actions to the category and set the action context + [notificationCategory setActions:categoryArray forContext:UIUserNotificationActionContextDefault]; + + // Set the actions to present in a minimal context + [notificationCategory setActions:minimalCategoryArray forContext:UIUserNotificationActionContextMinimal]; + + NSLog(@"Adding category %@", key); + [categories addObject:notificationCategory]; + } + + } + + if ([[UIApplication sharedApplication]respondsToSelector:@selector(registerUserNotificationSettings:)]) { + UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UserNotificationTypes categories:categories]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; + } + + // Read GoogleService-Info.plist + NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; + + // Load the file content and read the data into arrays + NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path]; + fcmSenderId = [dict objectForKey:@"GCM_SENDER_ID"]; + BOOL isGcmEnabled = [[dict valueForKey:@"IS_GCM_ENABLED"] boolValue]; + + NSLog(@"FCM Sender ID %@", fcmSenderId); + + // GCM options + [self setFcmSenderId: fcmSenderId]; + if(isGcmEnabled && [[self fcmSenderId] length] > 0) { + NSLog(@"Using FCM Notification"); + [self setUsesFCM: YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + if([FIRApp defaultApp] == nil) + [FIRApp configure]; + [self initRegistration]; + }); + } else { + NSLog(@"Using APNS Notification"); + [self setUsesFCM:NO]; + } + id fcmSandboxArg = [iosOptions objectForKey:@"fcmSandbox"]; + + [self setFcmSandbox:@NO]; + if ([self usesFCM] && + (([fcmSandboxArg isKindOfClass:[NSString class]] && [fcmSandboxArg isEqualToString:@"true"]) || + [fcmSandboxArg boolValue])) + { + NSLog(@"Using FCM Sandbox"); + [self setFcmSandbox:@YES]; + } + + if (notificationMessage) { // if there is a pending startup notification + dispatch_async(dispatch_get_main_queue(), ^{ + // delay to allow JS event handlers to be setup + [self performSelector:@selector(notificationReceived) withObject:nil afterDelay: 0.5]; + }); + } + }]; +} + +- (UIMutableUserNotificationAction *)createAction:(NSDictionary *)dictionary { + + UIMutableUserNotificationAction *myAction = [[UIMutableUserNotificationAction alloc] init]; + + myAction = [[UIMutableUserNotificationAction alloc] init]; + myAction.identifier = [dictionary objectForKey:@"callback"]; + myAction.title = [dictionary objectForKey:@"title"]; + id mode =[dictionary objectForKey:@"foreground"]; + if (mode == nil || ([mode isKindOfClass:[NSString class]] && [mode isEqualToString:@"false"]) || ![mode boolValue]) { + myAction.activationMode = UIUserNotificationActivationModeBackground; + } else { + myAction.activationMode = UIUserNotificationActivationModeForeground; + } + id destructive = [dictionary objectForKey:@"destructive"]; + if (destructive == nil || ([destructive isKindOfClass:[NSString class]] && [destructive isEqualToString:@"false"]) || ![destructive boolValue]) { + myAction.destructive = NO; + } else { + myAction.destructive = YES; + } + myAction.authenticationRequired = NO; + + return myAction; +} + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + if (self.callbackId == nil) { + NSLog(@"Unexpected call to didRegisterForRemoteNotificationsWithDeviceToken, ignoring: %@", deviceToken); + return; + } + NSLog(@"Push Plugin register success: %@", deviceToken); + + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] + stringByReplacingOccurrencesOfString:@">" withString:@""] + stringByReplacingOccurrencesOfString: @" " withString: @""]; + [results setValue:token forKey:@"deviceToken"]; + +#if !TARGET_IPHONE_SIMULATOR + // Get Bundle Info for Remote Registration (handy if you have more than one app) + [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"] forKey:@"appName"]; + [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forKey:@"appVersion"]; + + // Check what Notifications the user has turned on. We registered for all three, but they may have manually disabled some or all of them. + + NSUInteger rntypes = [[[UIApplication sharedApplication] currentUserNotificationSettings] types]; + + // Set the defaults to disabled unless we find otherwise... + NSString *pushBadge = @"disabled"; + NSString *pushAlert = @"disabled"; + NSString *pushSound = @"disabled"; + + // Check what Registered Types are turned on. This is a bit tricky since if two are enabled, and one is off, it will return a number 2... not telling you which + // one is actually disabled. So we are literally checking to see if rnTypes matches what is turned on, instead of by number. The "tricky" part is that the + // single notification types will only match if they are the ONLY one enabled. Likewise, when we are checking for a pair of notifications, it will only be + // true if those two notifications are on. This is why the code is written this way + if(rntypes & UIUserNotificationTypeBadge){ + pushBadge = @"enabled"; + } + if(rntypes & UIUserNotificationTypeAlert) { + pushAlert = @"enabled"; + } + if(rntypes & UIUserNotificationTypeSound) { + pushSound = @"enabled"; + } + + [results setValue:pushBadge forKey:@"pushBadge"]; + [results setValue:pushAlert forKey:@"pushAlert"]; + [results setValue:pushSound forKey:@"pushSound"]; + + // Get the users Device Model, Display Name, Token & Version Number + UIDevice *dev = [UIDevice currentDevice]; + [results setValue:dev.name forKey:@"deviceName"]; + [results setValue:dev.model forKey:@"deviceModel"]; + [results setValue:dev.systemVersion forKey:@"deviceSystemVersion"]; + + if(![self usesFCM]) { + [self registerWithToken: token]; + } +#endif +} + +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + if (self.callbackId == nil) { + NSLog(@"Unexpected call to didFailToRegisterForRemoteNotificationsWithError, ignoring: %@", error); + return; + } + NSLog(@"Push Plugin register failed"); + [self failWithMessage:self.callbackId withMsg:@"" withError:error]; +} + +- (void)notificationReceived { + NSLog(@"Notification received"); + + if (notificationMessage && self.callbackId != nil) + { + NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:4]; + NSMutableDictionary* additionalData = [NSMutableDictionary dictionaryWithCapacity:4]; + + + for (id key in notificationMessage) { + if ([key isEqualToString:@"aps"]) { + id aps = [notificationMessage objectForKey:@"aps"]; + + for(id key in aps) { + NSLog(@"Push Plugin key: %@", key); + id value = [aps objectForKey:key]; + + if ([key isEqualToString:@"alert"]) { + if ([value isKindOfClass:[NSDictionary class]]) { + for (id messageKey in value) { + id messageValue = [value objectForKey:messageKey]; + if ([messageKey isEqualToString:@"body"]) { + [message setObject:messageValue forKey:@"message"]; + } else if ([messageKey isEqualToString:@"title"]) { + [message setObject:messageValue forKey:@"title"]; + } else { + [additionalData setObject:messageValue forKey:messageKey]; + } + } + } + else { + [message setObject:value forKey:@"message"]; + } + } else if ([key isEqualToString:@"title"]) { + [message setObject:value forKey:@"title"]; + } else if ([key isEqualToString:@"badge"]) { + [message setObject:value forKey:@"count"]; + } else if ([key isEqualToString:@"sound"]) { + [message setObject:value forKey:@"sound"]; + } else if ([key isEqualToString:@"image"]) { + [message setObject:value forKey:@"image"]; + } else { + [additionalData setObject:value forKey:key]; + } + } + } else { + [additionalData setObject:[notificationMessage objectForKey:key] forKey:key]; + } + } + + if (isInline) { + [additionalData setObject:[NSNumber numberWithBool:YES] forKey:@"foreground"]; + } else { + [additionalData setObject:[NSNumber numberWithBool:NO] forKey:@"foreground"]; + } + + if (coldstart) { + [additionalData setObject:[NSNumber numberWithBool:YES] forKey:@"coldstart"]; + } else { + [additionalData setObject:[NSNumber numberWithBool:NO] forKey:@"coldstart"]; + } + + [message setObject:additionalData forKey:@"additionalData"]; + + // send notification message + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message]; + [pluginResult setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + + self.coldstart = NO; + self.notificationMessage = nil; + } +} + +- (void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand *)command +{ + NSMutableDictionary* options = [command.arguments objectAtIndex:0]; + int badge = [[options objectForKey:@"badge"] intValue] ?: 0; + + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge]; + + NSString* message = [NSString stringWithFormat:@"app badge count set to %d", badge]; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; +} + +- (void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand *)command +{ + NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber; + + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)badge]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; +} + +- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command +{ + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; + + NSString* message = [NSString stringWithFormat:@"cleared all notifications"]; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; +} + +- (void)hasPermission:(CDVInvokedUrlCommand *)command +{ + BOOL enabled = NO; + id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate; + if ([appDelegate respondsToSelector:@selector(userHasRemoteNotificationsEnabled)]) { + enabled = [appDelegate performSelector:@selector(userHasRemoteNotificationsEnabled)]; + } + + NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:1]; + [message setObject:[NSNumber numberWithBool:enabled] forKey:@"isEnabled"]; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; +} + +-(void)successWithMessage:(NSString *)myCallbackId withMsg:(NSString *)message +{ + if (myCallbackId != nil) + { + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:myCallbackId]; + } +} + +-(void)registerWithToken:(NSString*)token; { + // Send result to trigger 'registration' event but keep callback + NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2]; + [message setObject:token forKey:@"registrationId"]; + if ([self usesFCM]) { + [message setObject:@"FCM" forKey:@"registrationType"]; + } else { + [message setObject:@"APNS" forKey:@"registrationType"]; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message]; + [pluginResult setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; +} + + +-(void)failWithMessage:(NSString *)myCallbackId withMsg:(NSString *)message withError:(NSError *)error +{ + NSString *errorMessage = (error) ? [NSString stringWithFormat:@"%@ - %@", message, [error localizedDescription]] : message; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage]; + + [self.commandDelegate sendPluginResult:commandResult callbackId:myCallbackId]; +} + +-(void) finish:(CDVInvokedUrlCommand*)command +{ + NSLog(@"Push Plugin finish called"); + + [self.commandDelegate runInBackground:^ { + NSString* notId = [command.arguments objectAtIndex:0]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [NSTimer scheduledTimerWithTimeInterval:0.1 + target:self + selector:@selector(stopBackgroundTask:) + userInfo:notId + repeats:NO]; + }); + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +-(void)stopBackgroundTask:(NSTimer*)timer +{ + UIApplication *app = [UIApplication sharedApplication]; + + NSLog(@"Push Plugin stopBackgroundTask called"); + + if (handlerObj) { + NSLog(@"Push Plugin handlerObj"); + completionHandler = [handlerObj[[timer userInfo]] copy]; + if (completionHandler) { + NSLog(@"Push Plugin: stopBackgroundTask (remaining t: %f)", app.backgroundTimeRemaining); + completionHandler(UIBackgroundFetchResultNewData); + completionHandler = nil; + } + } +} + +@end |
