Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8E12FEF5F for ; Tue, 22 Jan 2013 01:58:05 +0000 (UTC) Received: (qmail 98286 invoked by uid 500); 22 Jan 2013 01:58:04 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 98074 invoked by uid 500); 22 Jan 2013 01:58:02 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: callback-dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 96652 invoked by uid 99); 22 Jan 2013 01:57:59 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 22 Jan 2013 01:57:59 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 5CBB8822AF6; Tue, 22 Jan 2013 01:57:59 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: filmaj@apache.org To: commits@cordova.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [10/52] [partial] support for 2.4.0rc1. "vendored" the platform libs in. added Gord and Braden as contributors. removed dependency on unzip and axed the old download-cordova code. Message-Id: <20130122015759.5CBB8822AF6@tyr.zones.apache.org> Date: Tue, 22 Jan 2013 01:57:59 +0000 (UTC) http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m new file mode 100644 index 0000000..217f611 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m @@ -0,0 +1,521 @@ +/* + 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 "CDVLocalStorage.h" +#import "CDV.h" + +@interface CDVLocalStorage () + +@property (nonatomic, readwrite, strong) NSMutableArray* backupInfo; // array of CDVBackupInfo objects +@property (nonatomic, readwrite, weak) id webviewDelegate; + +@end + +@implementation CDVLocalStorage + +@synthesize backupInfo, webviewDelegate; + +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings +{ + self = (CDVLocalStorage*)[super initWithWebView:theWebView settings:classSettings]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive) + name:UIApplicationWillResignActiveNotification object:nil]; + + self.backupInfo = [[self class] createBackupInfoWithCloudBackup:[(NSString*)[classSettings objectForKey:@"backupType"] isEqualToString:@"cloud"]]; + + // over-ride current webview delegate (for restore reasons) + self.webviewDelegate = theWebView.delegate; + theWebView.delegate = self; + } + + return self; +} + +#pragma mark - +#pragma mark Plugin interface methods + ++ (NSMutableArray*)createBackupInfoWithTargetDir:(NSString*)targetDir backupDir:(NSString*)backupDir targetDirNests:(BOOL)targetDirNests backupDirNests:(BOOL)backupDirNests rename:(BOOL)rename +{ + /* + This "helper" does so much work and has so many options it would probably be clearer to refactor the whole thing. + Basically, there are three database locations: + + 1. "Normal" dir -- LIB// + 2. "Caches" dir -- LIB/Caches/ + 3. "Backup" dir -- DOC/Backups/ + + And between these three, there are various migration paths, most of which only consider 2 of the 3, which is why this helper is based on 2 locations and has a notion of "direction". + */ + NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:3]; + + NSString* original; + NSString* backup; + CDVBackupInfo* backupItem; + + // ////////// LOCALSTORAGE + + original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0.localstorage":@"file__0.localstorage"]; + backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")]; + backup = [backup stringByAppendingPathComponent:(rename ? @"localstorage.appdata.db":@"file__0.localstorage")]; + + backupItem = [[CDVBackupInfo alloc] init]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"localStorage database"; + + [backupInfo addObject:backupItem]; + + // ////////// WEBSQL MAIN DB + + original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/Databases.db":@"Databases.db"]; + backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")]; + backup = [backup stringByAppendingPathComponent:(rename ? @"websqlmain.appdata.db":@"Databases.db")]; + + backupItem = [[CDVBackupInfo alloc] init]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql main database"; + + [backupInfo addObject:backupItem]; + + // ////////// WEBSQL DATABASES + + original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0":@"file__0"]; + backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")]; + backup = [backup stringByAppendingPathComponent:(rename ? @"websqldbs.appdata.db":@"file__0")]; + + backupItem = [[CDVBackupInfo alloc] init]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql databases"; + + [backupInfo addObject:backupItem]; + + return backupInfo; +} + ++ (NSMutableArray*)createBackupInfoWithCloudBackup:(BOOL)cloudBackup +{ + // create backup info from backup folder to caches folder + NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString* cacheFolder = [appLibraryFolder stringByAppendingPathComponent:@"Caches"]; + NSString* backupsFolder = [appDocumentsFolder stringByAppendingPathComponent:@"Backups"]; + + // create the backups folder, if needed + [[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder withIntermediateDirectories:YES attributes:nil error:nil]; + + [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:backupsFolder] skip:!cloudBackup]; + + return [self createBackupInfoWithTargetDir:cacheFolder backupDir:backupsFolder targetDirNests:NO backupDirNests:NO rename:YES]; +} + ++ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL*)URL skip:(BOOL)skip +{ + NSAssert(IsAtLeastiOSVersion(@"5.1"), @"Cannot mark files for NSURLIsExcludedFromBackupKey on iOS less than 5.1"); + + NSError* error = nil; + BOOL success = [URL setResourceValue:[NSNumber numberWithBool:skip] forKey:NSURLIsExcludedFromBackupKey error:&error]; + if (!success) { + NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); + } + return success; +} + ++ (BOOL)copyFrom:(NSString*)src to:(NSString*)dest error:(NSError * __autoreleasing*)error +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + + if (![fileManager fileExistsAtPath:src]) { + NSString* errorString = [NSString stringWithFormat:@"%@ file does not exist.", src]; + if (error != NULL) { + (*error) = [NSError errorWithDomain:kCDVLocalStorageErrorDomain + code:kCDVLocalStorageFileOperationError + userInfo:[NSDictionary dictionaryWithObject:errorString + forKey:NSLocalizedDescriptionKey]]; + } + return NO; + } + + // generate unique filepath in temp directory + CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef); + NSString* tempBackup = [[NSTemporaryDirectory () stringByAppendingPathComponent:(__bridge NSString*)uuidString] stringByAppendingPathExtension:@"bak"]; + CFRelease(uuidString); + CFRelease(uuidRef); + + BOOL destExists = [fileManager fileExistsAtPath:dest]; + + // backup the dest + if (destExists && ![fileManager copyItemAtPath:dest toPath:tempBackup error:error]) { + return NO; + } + + // remove the dest + if (destExists && ![fileManager removeItemAtPath:dest error:error]) { + return NO; + } + + // create path to dest + if (!destExists && ![fileManager createDirectoryAtPath:[dest stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:error]) { + return NO; + } + + // copy src to dest + if ([fileManager copyItemAtPath:src toPath:dest error:error]) { + // success - cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return YES; + } else { + // failure - we restore the temp backup file to dest + [fileManager copyItemAtPath:tempBackup toPath:dest error:error]; + // cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return NO; + } +} + +- (BOOL)shouldBackup +{ + for (CDVBackupInfo* info in self.backupInfo) { + if ([info shouldBackup]) { + return YES; + } + } + + return NO; +} + +- (BOOL)shouldRestore +{ + for (CDVBackupInfo* info in self.backupInfo) { + if ([info shouldRestore]) { + return YES; + } + } + + return NO; +} + +/* copy from webkitDbLocation to persistentDbLocation */ +- (void)backup:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + + NSError* __autoreleasing error = nil; + CDVPluginResult* result = nil; + NSString* message = nil; + + for (CDVBackupInfo* info in self.backupInfo) { + if ([info shouldBackup]) { + [[self class] copyFrom:info.original to:info.backup error:&error]; + + if (callbackId) { + if (error == nil) { + message = [NSString stringWithFormat:@"Backed up: %@", info.label]; + NSLog(@"%@", message); + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) backup: %@", info.label, [error localizedDescription]]; + NSLog(@"%@", message); + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + } + } + } +} + +/* copy from persistentDbLocation to webkitDbLocation */ +- (void)restore:(CDVInvokedUrlCommand*)command +{ + NSError* __autoreleasing error = nil; + CDVPluginResult* result = nil; + NSString* message = nil; + + for (CDVBackupInfo* info in self.backupInfo) { + if ([info shouldRestore]) { + [[self class] copyFrom:info.backup to:info.original error:&error]; + + if (error == nil) { + message = [NSString stringWithFormat:@"Restored: %@", info.label]; + NSLog(@"%@", message); + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) restore: %@", info.label, [error localizedDescription]]; + NSLog(@"%@", message); + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + } + } +} + ++ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType +{ + [self __verifyAndFixDatabaseLocations]; + [self __restoreLegacyDatabaseLocationsWithBackupType:backupType]; +} + ++ (void)__verifyAndFixDatabaseLocations +{ + NSBundle* mainBundle = [NSBundle mainBundle]; + NSString* bundlePath = [[mainBundle bundlePath] stringByDeletingLastPathComponent]; + NSString* bundleIdentifier = [[mainBundle infoDictionary] objectForKey:@"CFBundleIdentifier"]; + NSString* appPlistPath = [bundlePath stringByAppendingPathComponent:[NSString stringWithFormat:@"Library/Preferences/%@.plist", bundleIdentifier]]; + + NSMutableDictionary* appPlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:appPlistPath]; + BOOL modified = [[self class] __verifyAndFixDatabaseLocationsWithAppPlistDict:appPlistDict + bundlePath:bundlePath + fileManager:[NSFileManager defaultManager]]; + + if (modified) { + BOOL ok = [appPlistDict writeToFile:appPlistPath atomically:YES]; + [[NSUserDefaults standardUserDefaults] synchronize]; + NSLog(@"Fix applied for database locations?: %@", ok ? @"YES" : @"NO"); + } +} + ++ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict + bundlePath:(NSString*)bundlePath + fileManager:(NSFileManager*)fileManager +{ + NSString* libraryCaches = @"Library/Caches"; + NSString* libraryWebKit = @"Library/WebKit"; + + NSArray* keysToCheck = [NSArray arrayWithObjects: + @"WebKitLocalStorageDatabasePathPreferenceKey", + @"WebDatabaseDirectory", + nil]; + + BOOL dirty = NO; + + for (NSString* key in keysToCheck) { + NSString* value = [appPlistDict objectForKey:key]; + // verify key exists, and path is in app bundle, if not - fix + if ((value != nil) && ![value hasPrefix:bundlePath]) { + // the pathSuffix to use may be wrong - OTA upgrades from < 5.1 to 5.1 do keep the old path Library/WebKit, + // while Xcode synced ones do change the storage location to Library/Caches + NSString* newBundlePath = [bundlePath stringByAppendingPathComponent:libraryCaches]; + if (![fileManager fileExistsAtPath:newBundlePath]) { + newBundlePath = [bundlePath stringByAppendingPathComponent:libraryWebKit]; + } + [appPlistDict setValue:newBundlePath forKey:key]; + dirty = YES; + } + } + + return dirty; +} + ++ (void)__restoreLegacyDatabaseLocationsWithBackupType:(NSString*)backupType +{ + // on iOS 6, if you toggle between cloud/local backup, you must move database locations. Default upgrade from iOS5.1 to iOS6 is like a toggle from local to cloud. + if (!IsAtLeastiOSVersion(@"6.0")) { + return; + } + + NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + + NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:0]; + + if ([backupType isEqualToString:@"cloud"]) { + // We would like to restore old backups/caches databases to the new destination (nested in lib folder) + [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appDocumentsFolder stringByAppendingPathComponent:@"Backups"] targetDirNests:YES backupDirNests:NO rename:YES]]; + [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] targetDirNests:YES backupDirNests:NO rename:NO]]; + } else { + // For ios6 local backups we also want to restore from Backups dir -- but we don't need to do that here, since the plugin will do that itself. + [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] backupDir:appLibraryFolder targetDirNests:NO backupDirNests:YES rename:NO]]; + } + + NSFileManager* manager = [NSFileManager defaultManager]; + + for (CDVBackupInfo* info in backupInfo) { + if ([manager fileExistsAtPath:info.backup]) { + if ([info shouldRestore]) { + NSLog(@"Restoring old webstorage backup. From: '%@' To: '%@'.", info.backup, info.original); + [self copyFrom:info.backup to:info.original error:nil]; + } + NSLog(@"Removing old webstorage backup: '%@'.", info.backup); + [manager removeItemAtPath:info.backup error:nil]; + } + } + + [[NSUserDefaults standardUserDefaults] setBool:[backupType isEqualToString:@"cloud"] forKey:@"WebKitStoreWebDataForBackup"]; +} + +#pragma mark - +#pragma mark Notification handlers + +- (void)onResignActive +{ + UIDevice* device = [UIDevice currentDevice]; + NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"]; + + BOOL isMultitaskingSupported = [device respondsToSelector:@selector(isMultitaskingSupported)] && [device isMultitaskingSupported]; + + if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default) + exitsOnSuspend = [NSNumber numberWithBool:NO]; + } + + if (exitsOnSuspend) { + [self backup:nil]; + } else if (isMultitaskingSupported) { + __block UIBackgroundTaskIdentifier backgroundTaskID = UIBackgroundTaskInvalid; + + backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID]; + backgroundTaskID = UIBackgroundTaskInvalid; + NSLog (@"Background task to backup WebSQL/LocalStorage expired."); + }]; + CDVLocalStorage __weak* weakSelf = self; + [self.commandDelegate runInBackground:^{ + [weakSelf backup:nil]; + + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID]; + backgroundTaskID = UIBackgroundTaskInvalid; + }]; + } +} + +- (void)onAppTerminate +{ + [self onResignActive]; +} + +#pragma mark - +#pragma mark UIWebviewDelegate implementation and forwarding + +- (void)webViewDidStartLoad:(UIWebView*)theWebView +{ + [self restore:nil]; + + return [self.webviewDelegate webViewDidStartLoad:theWebView]; +} + +- (void)webViewDidFinishLoad:(UIWebView*)theWebView +{ + return [self.webviewDelegate webViewDidFinishLoad:theWebView]; +} + +- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + return [self.webviewDelegate webView:theWebView didFailLoadWithError:error]; +} + +- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} + +#pragma mark - +#pragma mark Over-rides + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock: +} + +@end + +#pragma mark - +#pragma mark CDVBackupInfo implementation + +@implementation CDVBackupInfo + +@synthesize original, backup, label; + +- (BOOL)file:(NSString*)aPath isNewerThanFile:(NSString*)bPath +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + NSError* __autoreleasing error = nil; + + NSDictionary* aPathAttribs = [fileManager attributesOfItemAtPath:aPath error:&error]; + NSDictionary* bPathAttribs = [fileManager attributesOfItemAtPath:bPath error:&error]; + + NSDate* aPathModDate = [aPathAttribs objectForKey:NSFileModificationDate]; + NSDate* bPathModDate = [bPathAttribs objectForKey:NSFileModificationDate]; + + if ((nil == aPathModDate) && (nil == bPathModDate)) { + return NO; + } + + return [aPathModDate compare:bPathModDate] == NSOrderedDescending || bPathModDate == nil; +} + +- (BOOL)item:(NSString*)aPath isNewerThanItem:(NSString*)bPath +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + + BOOL aPathIsDir = NO, bPathIsDir = NO; + BOOL aPathExists = [fileManager fileExistsAtPath:aPath isDirectory:&aPathIsDir]; + + [fileManager fileExistsAtPath:bPath isDirectory:&bPathIsDir]; + + if (!aPathExists) { + return NO; + } + + if (!(aPathIsDir && bPathIsDir)) { // just a file + return [self file:aPath isNewerThanFile:bPath]; + } + + // essentially we want rsync here, but have to settle for our poor man's implementation + // we get the files in aPath, and see if it is newer than the file in bPath + // (it is newer if it doesn't exist in bPath) if we encounter the FIRST file that is newer, + // we return YES + NSDirectoryEnumerator* directoryEnumerator = [fileManager enumeratorAtPath:aPath]; + NSString* path; + + while ((path = [directoryEnumerator nextObject])) { + NSString* aPathFile = [aPath stringByAppendingPathComponent:path]; + NSString* bPathFile = [bPath stringByAppendingPathComponent:path]; + + BOOL isNewer = [self file:aPathFile isNewerThanFile:bPathFile]; + if (isNewer) { + return YES; + } + } + + return NO; +} + +- (BOOL)shouldBackup +{ + return [self item:self.original isNewerThanItem:self.backup]; +} + +- (BOOL)shouldRestore +{ + return [self item:self.backup isNewerThanItem:self.original]; +} + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h new file mode 100644 index 0000000..7087d43 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h @@ -0,0 +1,104 @@ +/* + 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 +#import +#import "CDVPlugin.h" + +enum CDVHeadingStatus { + HEADINGSTOPPED = 0, + HEADINGSTARTING, + HEADINGRUNNING, + HEADINGERROR +}; +typedef NSUInteger CDVHeadingStatus; + +enum CDVLocationStatus { + PERMISSIONDENIED = 1, + POSITIONUNAVAILABLE, + TIMEOUT +}; +typedef NSUInteger CDVLocationStatus; + +// simple object to keep track of heading information +@interface CDVHeadingData : NSObject {} + +@property (nonatomic, assign) CDVHeadingStatus headingStatus; +@property (nonatomic, strong) CLHeading* headingInfo; +@property (nonatomic, strong) NSMutableArray* headingCallbacks; +@property (nonatomic, copy) NSString* headingFilter; +@property (nonatomic, strong) NSDate* headingTimestamp; +@property (assign) NSInteger timeout; + +@end + +// 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 { + @private BOOL __locationStarted; + @private BOOL __highAccuracyEnabled; + CDVHeadingData* headingData; + CDVLocationData* locationData; +} + +@property (nonatomic, strong) CLLocationManager* locationManager; +@property (strong) CDVHeadingData* headingData; +@property (nonatomic, strong) CDVLocationData* locationData; + +- (BOOL)hasHeadingSupport; +- (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; + +- (void)getHeading:(CDVInvokedUrlCommand*)command; +- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain; +- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command; +- (void)stopHeading:(CDVInvokedUrlCommand*)command; +- (void)startHeadingWithFilter:(CLLocationDegrees)filter; +- (void)locationManager:(CLLocationManager*)manager + didUpdateHeading :(CLHeading*)heading; + +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m new file mode 100644 index 0000000..9814f35 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m @@ -0,0 +1,633 @@ +/* + 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" +#import "CDVViewController.h" +#import "NSArray+Comparisons.h" + +#pragma mark Constants + +#define kPGLocationErrorDomain @"kPGLocationErrorDomain" +#define kPGLocationDesiredAccuracyKey @"desiredAccuracy" +#define kPGLocationForcePromptKey @"forcePrompt" +#define kPGLocationDistanceFilterKey @"distanceFilter" +#define kPGLocationFrequencyKey @"frequency" + +#pragma mark - +#pragma mark Categories + +@interface NSError (JSONMethods) + +- (NSString*)JSONRepresentation; + +@end + +@interface CLLocation (JSONMethods) + +- (NSString*)JSONRepresentation; + +@end + +@interface CLHeading (JSONMethods) + +- (NSString*)JSONRepresentation; + +@end + +#pragma mark - +#pragma mark CDVHeadingData + +@implementation CDVHeadingData + +@synthesize headingStatus, headingInfo, headingCallbacks, headingFilter, headingTimestamp, timeout; +- (CDVHeadingData*)init +{ + self = (CDVHeadingData*)[super init]; + if (self) { + self.headingStatus = HEADINGSTOPPED; + self.headingInfo = nil; + self.headingCallbacks = nil; + self.headingFilter = nil; + self.headingTimestamp = nil; + self.timeout = 10; + } + return self; +} + +@end + +@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, headingData, locationData; + +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView +{ + self = (CDVLocation*)[super initWithWebView:(UIWebView*)theWebView]; + if (self) { + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; // Tells the location manager to send updates to this object + __locationStarted = NO; + __highAccuracyEnabled = NO; + self.headingData = nil; + self.locationData = nil; + } + return self; +} + +- (BOOL)hasHeadingSupport +{ + BOOL headingInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(headingAvailable)]; // iOS 3.x + BOOL headingClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(headingAvailable)]; // iOS 4.x + + if (headingInstancePropertyAvailable) { // iOS 3.x + return [(id)self.locationManager headingAvailable]; + } else if (headingClassPropertyAvailable) { // iOS 4.x + return [CLLocationManager headingAvailable]; + } else { // iOS 2.x + return NO; + } +} + +- (BOOL)isAuthorized +{ + BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + + if (authorizationStatusClassPropertyAvailable) { + NSUInteger authStatus = [CLLocationManager authorizationStatus]; + 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; + } + + // 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 to distance filter to "none" - which should be the minimum for best results. + self.locationManager.distanceFilter = kCLDistanceFilterNone; + // Set desired accuracy to Best. + self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; + } else { + __highAccuracyEnabled = NO; + // TODO: Set distance filter to 10 meters? and desired accuracy to nearest ten meters? arbitrary. + self.locationManager.distanceFilter = 10; + self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; + } +} + +- (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 +{ + NSString* callbackId = command.callbackId; + BOOL enableHighAccuracy = [[command.arguments objectAtIndex: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.arguments objectAtIndex:0]; + BOOL enableHighAccuracy = [[command.arguments objectAtIndex: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.arguments objectAtIndex:0]; + + if (self.locationData && self.locationData.watchCallbacks && [self.locationData.watchCallbacks objectForKey:timerId]) { + [self.locationData.watchCallbacks removeObjectForKey:timerId]; + } +} + +- (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 numberWithInt: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]; + } +} + +// called to get the current heading +// Will call location manager to startUpdatingHeading if necessary + +- (void)getHeading:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil]; + NSNumber* filter = [options valueForKey:@"filter"]; + + if (filter) { + [self watchHeadingFilter:command]; + return; + } + if ([self hasHeadingSupport] == NO) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + // heading retrieval does is not affected by disabling locationServices and authorization of app for location services + if (!self.headingData) { + self.headingData = [[CDVHeadingData alloc] init]; + } + CDVHeadingData* hData = self.headingData; + + if (!hData.headingCallbacks) { + hData.headingCallbacks = [NSMutableArray arrayWithCapacity:1]; + } + // add the callbackId into the array so we can call back when get data + [hData.headingCallbacks addObject:callbackId]; + + if ((hData.headingStatus != HEADINGRUNNING) && (hData.headingStatus != HEADINGERROR)) { + // Tell the location manager to start notifying us of heading updates + [self startHeadingWithFilter:0.2]; + } else { + [self returnHeadingInfo:callbackId keepCallback:NO]; + } + } +} + +// called to request heading updates when heading changes by a certain amount (filter) +- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil]; + NSNumber* filter = [options valueForKey:@"filter"]; + CDVHeadingData* hData = self.headingData; + + if ([self hasHeadingSupport] == NO) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!hData) { + self.headingData = [[CDVHeadingData alloc] init]; + hData = self.headingData; + } + if (hData.headingStatus != HEADINGRUNNING) { + // Tell the location manager to start notifying us of heading updates + [self startHeadingWithFilter:[filter doubleValue]]; + } else { + // if already running check to see if due to existing watch filter + if (hData.headingFilter && ![hData.headingFilter isEqualToString:callbackId]) { + // new watch filter being specified + // send heading data one last time to clear old successCallback + [self returnHeadingInfo:hData.headingFilter keepCallback:NO]; + } + } + // save the new filter callback and update the headingFilter setting + hData.headingFilter = callbackId; + // check if need to stop and restart in order to change value??? + self.locationManager.headingFilter = [filter doubleValue]; + } +} + +- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain +{ + CDVPluginResult* result = nil; + CDVHeadingData* hData = self.headingData; + + self.headingData.headingTimestamp = [NSDate date]; + + if (hData && (hData.headingStatus == HEADINGERROR)) { + // return error + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + } else if (hData && (hData.headingStatus == HEADINGRUNNING) && hData.headingInfo) { + // if there is heading info, return it + CLHeading* hInfo = hData.headingInfo; + NSMutableDictionary* returnInfo = [NSMutableDictionary dictionaryWithCapacity:4]; + NSNumber* timestamp = [NSNumber numberWithDouble:([hInfo.timestamp timeIntervalSince1970] * 1000)]; + [returnInfo setObject:timestamp forKey:@"timestamp"]; + [returnInfo setObject:[NSNumber numberWithDouble:hInfo.magneticHeading] forKey:@"magneticHeading"]; + id trueHeading = __locationStarted ? (id)[NSNumber numberWithDouble : hInfo.trueHeading] : (id)[NSNull null]; + [returnInfo setObject:trueHeading forKey:@"trueHeading"]; + [returnInfo setObject:[NSNumber numberWithDouble:hInfo.headingAccuracy] forKey:@"headingAccuracy"]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo]; + [result setKeepCallbackAsBool:bRetain]; + } + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)stopHeading:(CDVInvokedUrlCommand*)command +{ + // CDVHeadingData* hData = self.headingData; + if (self.headingData && (self.headingData.headingStatus != HEADINGSTOPPED)) { + if (self.headingData.headingFilter) { + // callback one last time to clear callback + [self returnHeadingInfo:self.headingData.headingFilter keepCallback:NO]; + self.headingData.headingFilter = nil; + } + [self.locationManager stopUpdatingHeading]; + NSLog(@"heading STOPPED"); + self.headingData = nil; + } +} + +// helper method to check the orientation and start updating headings +- (void)startHeadingWithFilter:(CLLocationDegrees)filter +{ + if ([self.locationManager respondsToSelector:@selector(headingOrientation)]) { + UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation]; + if (currentOrientation != UIDeviceOrientationUnknown) { + CDVViewController* cdvViewController = (CDVViewController*)self.viewController; + + if ([cdvViewController supportsOrientation:currentOrientation]) { + self.locationManager.headingOrientation = (CLDeviceOrientation)currentOrientation; + // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same + } + } + } + self.locationManager.headingFilter = filter; + [self.locationManager startUpdatingHeading]; + self.headingData.headingStatus = HEADINGSTARTING; +} + +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager +{ + return YES; +} + +- (void)locationManager:(CLLocationManager*)manager + didUpdateHeading:(CLHeading*)heading +{ + CDVHeadingData* hData = self.headingData; + + // normally we would clear the delegate to stop getting these notifications, but + // we are sharing a CLLocationManager to get location data as well, so we do a nil check here + // ideally heading and location should use their own CLLocationManager instances + if (hData == nil) { + return; + } + + // save the data for next call into getHeadingData + hData.headingInfo = heading; + BOOL bTimeout = NO; + if (!hData.headingFilter && hData.headingTimestamp) { + bTimeout = fabs([hData.headingTimestamp timeIntervalSinceNow]) > hData.timeout; + } + + if (hData.headingStatus == HEADINGSTARTING) { + hData.headingStatus = HEADINGRUNNING; // so returnHeading info will work + + // this is the first update + for (NSString* callbackId in hData.headingCallbacks) { + [self returnHeadingInfo:callbackId keepCallback:NO]; + } + + [hData.headingCallbacks removeAllObjects]; + } + if (hData.headingFilter) { + [self returnHeadingInfo:hData.headingFilter keepCallback:YES]; + } else if (bTimeout) { + [self stopHeading:nil]; + } + hData.headingStatus = HEADINGRUNNING; // to clear any error +} + +- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error +{ + NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]); + + // Compass Error + if ([error code] == kCLErrorHeadingFailure) { + CDVHeadingData* hData = self.headingData; + if (hData) { + if (hData.headingStatus == HEADINGSTARTING) { + // heading error during startup - report error + for (NSString* callbackId in hData.headingCallbacks) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + + [hData.headingCallbacks removeAllObjects]; + } // else for frequency watches next call to getCurrentHeading will report error + if (hData.headingFilter) { + CDVPluginResult* resultFilter = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0]; + [self.commandDelegate sendPluginResult:resultFilter callbackId:hData.headingFilter]; + } + hData.headingStatus = HEADINGERROR; + } + } + // Location Error + else { + 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]]; + } + } + + [self.locationManager stopUpdatingLocation]; + __locationStarted = NO; +} + +- (void)dealloc +{ + self.locationManager.delegate = nil; +} + +- (void)onReset +{ + [self _stopLocation]; + [self.locationManager stopUpdatingHeading]; + self.headingData = nil; +} + +@end + +#pragma mark - +#pragma mark CLLocation(JSONMethods) + +@implementation CLLocation (JSONMethods) + +- (NSString*)JSONRepresentation +{ + return [NSString stringWithFormat: + @"{ timestamp: %.00f, \ + coords: { latitude: %f, longitude: %f, altitude: %.02f, heading: %.02f, speed: %.02f, accuracy: %.02f, altitudeAccuracy: %.02f } \ + }", + [self.timestamp timeIntervalSince1970] * 1000.0, + self.coordinate.latitude, + self.coordinate.longitude, + self.altitude, + self.course, + self.speed, + self.horizontalAccuracy, + self.verticalAccuracy + ]; +} + +@end + +#pragma mark NSError(JSONMethods) + +@implementation NSError (JSONMethods) + +- (NSString*)JSONRepresentation +{ + return [NSString stringWithFormat: + @"{ code: %d, message: '%@'}", + self.code, + [self localizedDescription] + ]; +} + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h b/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h new file mode 100644 index 0000000..eeba63c --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/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 "CDVPlugin.h" + +@interface CDVLogger : CDVPlugin + +- (void)logLevel:(CDVInvokedUrlCommand*)command; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m b/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m new file mode 100644 index 0000000..a37cf8a --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/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 "CDV.h" + +@implementation CDVLogger + +/* log a message */ +- (void)logLevel:(CDVInvokedUrlCommand*)command +{ + id level = [command.arguments objectAtIndex:0]; + id message = [command.arguments objectAtIndex:1]; + + if ([level isEqualToString:@"LOG"]) { + NSLog(@"%@", message); + } else { + NSLog(@"%@: %@", level, message); + } +} + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h new file mode 100644 index 0000000..1eedb54 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h @@ -0,0 +1,36 @@ +/* + 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 +#import +#import +#import "CDVPlugin.h" + +@interface CDVNotification : CDVPlugin {} + +- (void)alert:(CDVInvokedUrlCommand*)command; +- (void)confirm:(CDVInvokedUrlCommand*)command; +- (void)vibrate:(CDVInvokedUrlCommand*)command; + +@end + +@interface CDVAlertView : UIAlertView {} +@property (nonatomic, copy) NSString* callbackId; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m new file mode 100644 index 0000000..992239e --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m @@ -0,0 +1,109 @@ +/* + 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" +#import "NSDictionary+Extensions.h" + +@implementation CDVNotification + +- (void)showDialogWithMessage:(NSString*)message title:(NSString*)title buttons:(NSString*)buttons callbackId:(NSString*)callbackId +{ + CDVAlertView* alertView = [[CDVAlertView alloc] + initWithTitle:title + message:message + delegate:self + cancelButtonTitle:nil + otherButtonTitles:nil]; + + alertView.callbackId = callbackId; + + NSArray* labels = [buttons componentsSeparatedByString:@","]; + int count = [labels count]; + + for (int n = 0; n < count; n++) { + [alertView addButtonWithTitle:[labels objectAtIndex:n]]; + } + + [alertView show]; +} + +- (void)alert:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSArray* arguments = command.arguments; + int argc = [arguments count]; + + NSString* message = argc > 0 ? [arguments objectAtIndex:0] : nil; + NSString* title = argc > 1 ? [arguments objectAtIndex:1] : nil; + NSString* buttons = argc > 2 ? [arguments objectAtIndex:2] : nil; + + if (!title) { + title = NSLocalizedString(@"Alert", @"Alert"); + } + if (!buttons) { + buttons = NSLocalizedString(@"OK", @"OK"); + } + + [self showDialogWithMessage:message title:title buttons:buttons callbackId:callbackId]; +} + +- (void)confirm:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSArray* arguments = command.arguments; + int argc = [arguments count]; + + NSString* message = argc > 0 ? [arguments objectAtIndex:0] : nil; + NSString* title = argc > 1 ? [arguments objectAtIndex:1] : nil; + NSString* buttons = argc > 2 ? [arguments objectAtIndex:2] : nil; + + if (!title) { + title = NSLocalizedString(@"Confirm", @"Confirm"); + } + if (!buttons) { + buttons = NSLocalizedString(@"OK,Cancel", @"OK,Cancel"); + } + + [self showDialogWithMessage:message title:title buttons:buttons callbackId:callbackId]; +} + +/** + Callback invoked when an alert dialog's buttons are clicked. + Passes the index + label back to JS + */ +- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + CDVAlertView* cdvAlertView = (CDVAlertView*)alertView; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:++buttonIndex]; + + [self.commandDelegate sendPluginResult:result callbackId:cdvAlertView.callbackId]; +} + +- (void)vibrate:(CDVInvokedUrlCommand*)command +{ + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +} + +@end + +@implementation CDVAlertView + +@synthesize callbackId; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h new file mode 100644 index 0000000..8c59a2b --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h @@ -0,0 +1,64 @@ +/* + 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 +#import +#import "CDVPluginResult.h" +#import "NSMutableArray+QueueAdditions.h" +#import "CDVCommandDelegate.h" + +#define CDVPluginHandleOpenURLNotification @"CDVPluginHandleOpenURLNotification" +#define CDVPluginResetNotification @"CDVPluginResetNotification" +#define CDVLocalNotification @"CDVLocalNotification" + +@interface CDVPlugin : NSObject {} + +@property (nonatomic, weak) UIWebView* webView; +@property (nonatomic, strong) NSDictionary* settings; +@property (nonatomic, weak) UIViewController* viewController; +@property (nonatomic, weak) id commandDelegate; + +@property (readonly, assign) BOOL hasPendingOperation; + +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings; +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView; + +- (void)handleOpenURL:(NSNotification*)notification; +- (void)onAppTerminate; +- (void)onMemoryWarning; +- (void)onReset; +- (void)dispose; + +/* + // see initWithWebView implementation + - (void) onPause {} + - (void) onResume {} + - (void) onOrientationWillChange {} + - (void) onOrientationDidChange {} + - (void)didReceiveLocalNotification:(NSNotification *)notification; + */ + +- (id)appDelegate; + +// TODO(agrieve): Deprecate these in favour of using CDVCommandDelegate directly. +- (NSString*)writeJavascript:(NSString*)javascript; +- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId; +- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m new file mode 100644 index 0000000..53023c7 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m @@ -0,0 +1,148 @@ +/* + 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 "CDVPlugin.h" + +@interface CDVPlugin () + +@property (readwrite, assign) BOOL hasPendingOperation; + +@end + +@implementation CDVPlugin +@synthesize webView, settings, viewController, commandDelegate, hasPendingOperation; + +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings +{ + self = [self initWithWebView:theWebView]; + if (self) { + self.settings = classSettings; + self.hasPendingOperation = NO; + } + return self; +} + +- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView +{ + self = [super init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:nil]; + + self.webView = theWebView; + + // You can listen to more app notifications, see: + // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 + + /* + // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + // Added in 2.3.0+ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil]; + + */ + } + return self; +} + +- (void)dispose +{ + viewController = nil; + commandDelegate = nil; + webView = nil; +} + +/* +// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts +- (void) onPause {} +- (void) onResume {} +- (void) onOrientationWillChange {} +- (void) onOrientationDidChange {} +*/ + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)handleOpenURL:(NSNotification*)notification +{ + // override to handle urls sent to your app + // register your url schemes in your App-Info.plist + + NSURL* url = [notification object]; + + if ([url isKindOfClass:[NSURL class]]) { + /* Do your thing! */ + } +} + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)onAppTerminate +{ + // override this if you need to do any cleanup on app exit +} + +- (void)onMemoryWarning +{ + // override to remove caches, etc +} + +- (void)onReset +{ + // Override to cancel any long-running requests when the WebView navigates or refreshes. +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock: +} + +- (id)appDelegate +{ + return [[UIApplication sharedApplication] delegate]; +} + +- (NSString*)writeJavascript:(NSString*)javascript +{ + return [self.webView stringByEvaluatingJavaScriptFromString:javascript]; +} + +- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId +{ + [self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]]; + return @""; +} + +- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId +{ + [self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]]; + return @""; +} + +// default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything. +//- (void)didReceiveLocalNotification:(NSNotification *)notification +//{ +// // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification +//} + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h new file mode 100644 index 0000000..8683205 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h @@ -0,0 +1,61 @@ +/* + 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 + +typedef enum { + CDVCommandStatus_NO_RESULT = 0, + CDVCommandStatus_OK, + CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION, + CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION, + CDVCommandStatus_INSTANTIATION_EXCEPTION, + CDVCommandStatus_MALFORMED_URL_EXCEPTION, + CDVCommandStatus_IO_EXCEPTION, + CDVCommandStatus_INVALID_ACTION, + CDVCommandStatus_JSON_EXCEPTION, + CDVCommandStatus_ERROR +} CDVCommandStatus; + +@interface CDVPluginResult : NSObject {} + +@property (nonatomic, strong, readonly) NSNumber* status; +@property (nonatomic, strong, readonly) id message; +@property (nonatomic, strong) NSNumber* keepCallback; + +- (CDVPluginResult*)init; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode; + ++ (void)setVerbose:(BOOL)verbose; ++ (BOOL)isVerbose; + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback; + +- (NSString*)toJSONString; +- (NSString*)toSuccessCallbackString:(NSString*)callbackId; +- (NSString*)toErrorCallbackString:(NSString*)callbackId; + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m new file mode 100644 index 0000000..d9ba08f --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m @@ -0,0 +1,181 @@ +/* + 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 "CDVPluginResult.h" +#import "CDVJSON.h" +#import "CDVDebug.h" +#import "NSData+Base64.h" + +@interface CDVPluginResult () + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage; + +@end + +@implementation CDVPluginResult +@synthesize status, message, keepCallback; + +static NSArray* org_apache_cordova_CommandStatusMsgs; + ++ (void)initialize +{ + org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result", + @"OK", + @"Class not found", + @"Illegal access", + @"Instantiation error", + @"Malformed url", + @"IO error", + @"Invalid action", + @"JSON error", + @"Error", + nil]; +} + +- (CDVPluginResult*)init +{ + return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil]; +} + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage +{ + self = [super init]; + if (self) { + status = [NSNumber numberWithInt:statusOrdinal]; + message = theMessage; + keepCallback = [NSNumber numberWithBool:NO]; + } + return self; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal +{ + return [[self alloc] initWithStatus:statusOrdinal message:[org_apache_cordova_CommandStatusMsgs objectAtIndex:statusOrdinal]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage +{ + NSDictionary* arrDict = [NSDictionary dictionaryWithObjectsAndKeys: + @"ArrayBuffer", @"CDVType", + [theMessage base64EncodedString], @"data", + nil]; + + return [[self alloc] initWithStatus:statusOrdinal message:arrDict]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode +{ + NSDictionary* errDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"code"]; + + return [[self alloc] initWithStatus:statusOrdinal message:errDict]; +} + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback +{ + [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]]; +} + +- (NSString*)toJSONString +{ + NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: + self.status, @"status", + self.message ? self. message:[NSNull null], @"message", + self.keepCallback, @"keepCallback", + nil]; + + NSError* error = nil; + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict + options:NSJSONWritingPrettyPrinted + error:&error]; + NSString* resultString = nil; + + if (error != nil) { + NSLog(@"toJSONString error: %@", [error localizedDescription]); + } else { + resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } + + if ([[self class] isVerbose]) { + NSLog(@"PluginResult:toJSONString - %@", resultString); + } + return resultString; +} + +- (NSString*)toSuccessCallbackString:(NSString*)callbackId +{ + NSString* successCB = [NSString stringWithFormat:@"cordova.callbackSuccess('%@',%@);", callbackId, [self toJSONString]]; + + if ([[self class] isVerbose]) { + NSLog(@"PluginResult toSuccessCallbackString: %@", successCB); + } + return successCB; +} + +- (NSString*)toErrorCallbackString:(NSString*)callbackId +{ + NSString* errorCB = [NSString stringWithFormat:@"cordova.callbackError('%@',%@);", callbackId, [self toJSONString]]; + + if ([[self class] isVerbose]) { + NSLog(@"PluginResult toErrorCallbackString: %@", errorCB); + } + return errorCB; +} + +static BOOL gIsVerbose = NO; ++ (void)setVerbose:(BOOL)verbose +{ + gIsVerbose = verbose; +} + ++ (BOOL)isVerbose +{ + return gIsVerbose; +} + +@end http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h ---------------------------------------------------------------------- diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h b/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h new file mode 100644 index 0000000..01a95c3 --- /dev/null +++ b/lib/cordova-ios/CordovaLib/Classes/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 +#import +#import + +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