diff options
Diffstat (limited to 'iPhone/CordovaLib/Classes/CDVViewController.m')
-rwxr-xr-x | iPhone/CordovaLib/Classes/CDVViewController.m | 933 |
1 files changed, 0 insertions, 933 deletions
diff --git a/iPhone/CordovaLib/Classes/CDVViewController.m b/iPhone/CordovaLib/Classes/CDVViewController.m deleted file mode 100755 index 94f4552..0000000 --- a/iPhone/CordovaLib/Classes/CDVViewController.m +++ /dev/null @@ -1,933 +0,0 @@ -/* - 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 <objc/message.h> -#import "CDV.h" -#import "CDVCommandDelegateImpl.h" -#import "CDVConfigParser.h" -#import "CDVUserAgentUtil.h" -#import "CDVWebViewDelegate.h" - -#define degreesToRadian(x) (M_PI * (x) / 180.0) - -@interface CDVViewController () { - NSInteger _userAgentLockToken; - CDVWebViewDelegate* _webViewDelegate; -} - -@property (nonatomic, readwrite, strong) NSXMLParser* configParser; -@property (nonatomic, readwrite, strong) NSMutableDictionary* settings; -@property (nonatomic, readwrite, strong) CDVWhitelist* whitelist; -@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects; -@property (nonatomic, readwrite, strong) NSArray* startupPluginNames; -@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap; -@property (nonatomic, readwrite, strong) NSArray* supportedOrientations; -@property (nonatomic, readwrite, assign) BOOL loadFromString; - -@property (readwrite, assign) BOOL initialized; - -@property (atomic, strong) NSURL* openURL; - -@end - -@implementation CDVViewController - -@synthesize webView, supportedOrientations; -@synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames; -@synthesize configParser, settings, loadFromString; -@synthesize useSplashScreen; -@synthesize wwwFolderName, startPage, initialized, openURL; -@synthesize commandDelegate = _commandDelegate; -@synthesize commandQueue = _commandQueue; - -- (void)__init -{ - if ((self != nil) && !self.initialized) { - _commandQueue = [[CDVCommandQueue alloc] initWithViewController:self]; - _commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:) - name:UIApplicationWillTerminateNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:) - name:UIApplicationWillResignActiveNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:) - name:UIApplicationDidBecomeActiveNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:) - name:UIApplicationWillEnterForegroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:) - name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; - - // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist - self.supportedOrientations = [self parseInterfaceOrientations: - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]]; - - [self printMultitaskingInfo]; - [self printDeprecationNotice]; - self.initialized = YES; - - // load config.xml settings - [self loadSettings]; - useSplashScreen = YES; - } -} - -- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil -{ - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - [self __init]; - return self; -} - -- (id)init -{ - self = [super init]; - [self __init]; - return self; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:self - selector:@selector(keyboardWillShowOrHide:) - name:UIKeyboardWillShowNotification - object:nil]; - [nc addObserver:self - selector:@selector(keyboardWillShowOrHide:) - name:UIKeyboardWillHideNotification - object:nil]; -} - -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil]; - [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil]; -} - -- (void)keyboardWillShowOrHide:(NSNotification*)notif -{ - if (![@"true" isEqualToString : self.settings[@"KeyboardShrinksView"]]) { - return; - } - BOOL showEvent = [notif.name isEqualToString:UIKeyboardWillShowNotification]; - - CGRect keyboardFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; - keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil]; - - CGRect newFrame = self.view.bounds; - if (showEvent) { - newFrame.size.height -= keyboardFrame.size.height; - } - self.webView.frame = newFrame; - self.webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, -keyboardFrame.size.height, 0); -} - -- (void)printDeprecationNotice -{ - if (!IsAtLeastiOSVersion(@"5.0")) { - NSLog(@"CRITICAL: For Cordova 2.0, you will need to upgrade to at least iOS 5.0 or greater. Your current version of iOS is %@.", - [[UIDevice currentDevice] systemVersion] - ); - } -} - -- (void)printMultitaskingInfo -{ - UIDevice* device = [UIDevice currentDevice]; - BOOL backgroundSupported = NO; - - if ([device respondsToSelector:@selector(isMultitaskingSupported)]) { - backgroundSupported = device.multitaskingSupported; - } - - NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"]; - if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default) - exitsOnSuspend = [NSNumber numberWithBool:NO]; - } - - NSLog(@"Multi-tasking -> Device: %@, App: %@", (backgroundSupported ? @"YES" : @"NO"), (![exitsOnSuspend intValue]) ? @"YES" : @"NO"); -} - -- (BOOL)URLisAllowed:(NSURL*)url -{ - if (self.whitelist == nil) { - return YES; - } - - return [self.whitelist URLIsAllowed:url]; -} - -- (void)loadSettings -{ - CDVConfigParser* delegate = [[CDVConfigParser alloc] init]; - - // read from config.xml in the app bundle - NSString* path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"xml"]; - - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { - NSAssert(NO, @"ERROR: config.xml does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project."); - return; - } - - NSURL* url = [NSURL fileURLWithPath:path]; - - configParser = [[NSXMLParser alloc] initWithContentsOfURL:url]; - if (configParser == nil) { - NSLog(@"Failed to initialize XML parser."); - return; - } - [configParser setDelegate:((id < NSXMLParserDelegate >)delegate)]; - [configParser parse]; - - // Get the plugin dictionary, whitelist and settings from the delegate. - self.pluginsMap = delegate.pluginsDict; - self.startupPluginNames = delegate.startupPluginNames; - self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts]; - self.settings = delegate.settings; - - // And the start folder/page. - self.wwwFolderName = @"www"; - self.startPage = delegate.startPage; - if (self.startPage == nil) { - self.startPage = @"index.html"; - } - - // Initialize the plugin objects dict. - self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20]; -} - -// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. -- (void)viewDidLoad -{ - [super viewDidLoad]; - - NSURL* appURL = nil; - NSString* loadErr = nil; - - if ([self.startPage rangeOfString:@"://"].location != NSNotFound) { - appURL = [NSURL URLWithString:self.startPage]; - } else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) { - appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]]; - } else { - NSString* startFilePath = [self.commandDelegate pathForResource:self.startPage]; - if (startFilePath == nil) { - loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage]; - NSLog(@"%@", loadErr); - self.loadFromString = YES; - appURL = nil; - } else { - appURL = [NSURL fileURLWithPath:startFilePath]; - } - } - - // // Fix the iOS 5.1 SECURITY_ERR bug (CB-347), this must be before the webView is instantiated //// - - NSString* backupWebStorageType = @"cloud"; // default value - - id backupWebStorage = self.settings[@"BackupWebStorage"]; - if ([backupWebStorage isKindOfClass:[NSString class]]) { - backupWebStorageType = backupWebStorage; - } - self.settings[@"BackupWebStorage"] = backupWebStorageType; - - if (IsAtLeastiOSVersion(@"5.1")) { - [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType]; - } - - // // Instantiate the WebView /////////////// - - [self createGapView]; - - // ///////////////// - - NSNumber* enableLocation = [self.settings objectForKey:@"EnableLocation"]; - NSString* enableViewportScale = [self.settings objectForKey:@"EnableViewportScale"]; - NSNumber* allowInlineMediaPlayback = [self.settings objectForKey:@"AllowInlineMediaPlayback"]; - BOOL mediaPlaybackRequiresUserAction = YES; // default value - if ([self.settings objectForKey:@"MediaPlaybackRequiresUserAction"]) { - mediaPlaybackRequiresUserAction = [(NSNumber*)[settings objectForKey:@"MediaPlaybackRequiresUserAction"] boolValue]; - } - BOOL hideKeyboardFormAccessoryBar = NO; // default value - if ([self.settings objectForKey:@"HideKeyboardFormAccessoryBar"]) { - hideKeyboardFormAccessoryBar = [(NSNumber*)[settings objectForKey:@"HideKeyboardFormAccessoryBar"] boolValue]; - } - - self.webView.scalesPageToFit = [enableViewportScale boolValue]; - - /* - * Fire up the GPS Service right away as it takes a moment for data to come back. - */ - - if ([enableLocation boolValue]) { - NSLog(@"Deprecated: The 'EnableLocation' boolean property is deprecated in 2.5.0, and will be removed in 3.0.0. Use the 'onload' boolean attribute (of the CDVLocation plugin."); - [[self.commandDelegate getCommandInstance:@"Geolocation"] getLocation:[CDVInvokedUrlCommand new]]; - } - - if (hideKeyboardFormAccessoryBar) { - __weak CDVViewController* weakSelf = self; - [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* notification) { - // we can't hide it here because the accessory bar hasn't been created yet, so we delay on the queue - [weakSelf performSelector:@selector(hideKeyboardFormAccessoryBar) withObject:nil afterDelay:0]; - }]; - } - - /* - * Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup. - */ - if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) || - ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) { - [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])]; - } - - /* - * This is for iOS 4.x, where you can allow inline <video> and <audio>, and also autoplay them - */ - if ([allowInlineMediaPlayback boolValue] && [self.webView respondsToSelector:@selector(allowsInlineMediaPlayback)]) { - self.webView.allowsInlineMediaPlayback = YES; - } - if ((mediaPlaybackRequiresUserAction == NO) && [self.webView respondsToSelector:@selector(mediaPlaybackRequiresUserAction)]) { - self.webView.mediaPlaybackRequiresUserAction = NO; - } - - // By default, overscroll bouncing is allowed. - // UIWebViewBounce has been renamed to DisallowOverscroll, but both are checked. - BOOL bounceAllowed = YES; - NSNumber* disallowOverscroll = [self.settings objectForKey:@"DisallowOverscroll"]; - if (disallowOverscroll == nil) { - NSNumber* bouncePreference = [self.settings objectForKey:@"UIWebViewBounce"]; - bounceAllowed = (bouncePreference == nil || [bouncePreference boolValue]); - } else { - bounceAllowed = ![disallowOverscroll boolValue]; - } - - // prevent webView from bouncing - // based on the DisallowOverscroll/UIWebViewBounce key in config.xml - if (!bounceAllowed) { - if ([self.webView respondsToSelector:@selector(scrollView)]) { - ((UIScrollView*)[self.webView scrollView]).bounces = NO; - } else { - for (id subview in self.webView.subviews) { - if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { - ((UIScrollView*)subview).bounces = NO; - } - } - } - } - - /* - * iOS 6.0 UIWebView properties - */ - if (IsAtLeastiOSVersion(@"6.0")) { - BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES - if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] != nil) { - if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"]) { - keyboardDisplayRequiresUserAction = [(NSNumber*)[self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] boolValue]; - } - } - - // property check for compiling under iOS < 6 - if ([self.webView respondsToSelector:@selector(setKeyboardDisplayRequiresUserAction:)]) { - [self.webView setValue:[NSNumber numberWithBool:keyboardDisplayRequiresUserAction] forKey:@"keyboardDisplayRequiresUserAction"]; - } - - BOOL suppressesIncrementalRendering = NO; // SuppressesIncrementalRendering - defaults to NO - if ([self.settings objectForKey:@"SuppressesIncrementalRendering"] != nil) { - if ([self.settings objectForKey:@"SuppressesIncrementalRendering"]) { - suppressesIncrementalRendering = [(NSNumber*)[self.settings objectForKey:@"SuppressesIncrementalRendering"] boolValue]; - } - } - - // property check for compiling under iOS < 6 - if ([self.webView respondsToSelector:@selector(setSuppressesIncrementalRendering:)]) { - [self.webView setValue:[NSNumber numberWithBool:suppressesIncrementalRendering] forKey:@"suppressesIncrementalRendering"]; - } - } - - if ([self.startupPluginNames count] > 0) { - [CDVTimer start:@"TotalPluginStartup"]; - - for (NSString* pluginName in self.startupPluginNames) { - [CDVTimer start:pluginName]; - [self getCommandInstance:pluginName]; - [CDVTimer stop:pluginName]; - } - - [CDVTimer stop:@"TotalPluginStartup"]; - } - - // TODO: Remove this explicit instantiation once we move to cordova-CLI. - if (useSplashScreen) { - [self getCommandInstance:@"splashscreen"]; - } - - // ///////////////// - [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) { - _userAgentLockToken = lockToken; - [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken]; - if (!loadErr) { - NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0]; - [self.webView loadRequest:appReq]; - } else { - NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr]; - [self.webView loadHTMLString:html baseURL:nil]; - } - }]; -} - -- (void)hideKeyboardFormAccessoryBar -{ - NSArray* windows = [[UIApplication sharedApplication] windows]; - - for (UIWindow* window in windows) { - for (UIView* view in window.subviews) { - if ([[view description] hasPrefix:@"<UIPeripheralHostView"]) { - for (UIView* peripheralView in view.subviews) { - // hides the accessory bar - if ([[peripheralView description] hasPrefix:@"<UIWebFormAccessory"]) { - // remove the extra scroll space for the form accessory bar - CGRect newFrame = self.webView.scrollView.frame; - newFrame.size.height += peripheralView.frame.size.height; - self.webView.scrollView.frame = newFrame; - - // remove the form accessory bar - [peripheralView removeFromSuperview]; - } - // hides the thin grey line used to adorn the bar (iOS 6) - if ([[peripheralView description] hasPrefix:@"<UIImageView"]) { - [[peripheralView layer] setOpacity:0.0]; - } - } - } - } - } -} - -- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations -{ - NSMutableArray* result = [[NSMutableArray alloc] init]; - - if (orientations != nil) { - NSEnumerator* enumerator = [orientations objectEnumerator]; - NSString* orientationString; - - while (orientationString = [enumerator nextObject]) { - if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) { - [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; - } else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) { - [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]]; - } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) { - [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]]; - } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) { - [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]]; - } - } - } - - // default - if ([result count] == 0) { - [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; - } - - return result; -} - -- (NSInteger)mapIosOrientationToJsOrientation:(UIInterfaceOrientation)orientation -{ - switch (orientation) { - case UIInterfaceOrientationPortraitUpsideDown: - return 180; - - case UIInterfaceOrientationLandscapeLeft: - return -90; - - case UIInterfaceOrientationLandscapeRight: - return 90; - - case UIInterfaceOrientationPortrait: - return 0; - - default: - return 0; - } -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // First, ask the webview via JS if it supports the new orientation - NSString* jsCall = [NSString stringWithFormat: - @"window.shouldRotateToOrientation && window.shouldRotateToOrientation(%d);" - , [self mapIosOrientationToJsOrientation:interfaceOrientation]]; - NSString* res = [webView stringByEvaluatingJavaScriptFromString:jsCall]; - - if ([res length] > 0) { - return [res boolValue]; - } - - // if js did not handle the new orientation (no return value), use values from the plist (via supportedOrientations) - return [self supportsOrientation:interfaceOrientation]; -} - -- (BOOL)shouldAutorotate -{ - return YES; -} - -- (NSUInteger)supportedInterfaceOrientations -{ - NSUInteger ret = 0; - - if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) { - ret = ret | (1 << UIInterfaceOrientationPortrait); - } - if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown]) { - ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown); - } - if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]) { - ret = ret | (1 << UIInterfaceOrientationLandscapeRight); - } - if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft]) { - ret = ret | (1 << UIInterfaceOrientationLandscapeLeft); - } - - return ret; -} - -- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation -{ - return [self.supportedOrientations containsObject:[NSNumber numberWithInt:orientation]]; -} - -- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds -{ - return [[UIWebView alloc] initWithFrame:bounds]; -} - -- (NSString*)userAgent -{ - if (_userAgent == nil) { - NSString* originalUserAgent = [CDVUserAgentUtil originalUserAgent]; - // Use our address as a unique number to append to the User-Agent. - _userAgent = [NSString stringWithFormat:@"%@ (%lld)", originalUserAgent, (long long)self]; - } - return _userAgent; -} - -- (void)createGapView -{ - CGRect webViewBounds = self.view.bounds; - - webViewBounds.origin = self.view.bounds.origin; - - if (!self.webView) { - self.webView = [self newCordovaViewWithFrame:webViewBounds]; - self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - - [self.view addSubview:self.webView]; - [self.view sendSubviewToBack:self.webView]; - - _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self]; - self.webView.delegate = _webViewDelegate; - - // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set - [CDVURLProtocol registerViewController:self]; - } -} - -- (void)didReceiveMemoryWarning -{ - // iterate through all the plugin objects, and call hasPendingOperation - // if at least one has a pending operation, we don't call [super didReceiveMemoryWarning] - - NSEnumerator* enumerator = [self.pluginObjects objectEnumerator]; - CDVPlugin* plugin; - - BOOL doPurge = YES; - - while ((plugin = [enumerator nextObject])) { - if (plugin.hasPendingOperation) { - NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class])); - doPurge = NO; - } - } - - if (doPurge) { - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - } - - // Release any cached data, images, etc. that aren't in use. -} - -- (void)viewDidUnload -{ - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; - - self.webView.delegate = nil; - self.webView = nil; - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; -} - -#pragma mark UIWebViewDelegate - -/** - When web application loads Add stuff to the DOM, mainly the user-defined settings from the Settings.plist file, and - the device's data such as device ID, platform version, etc. - */ -- (void)webViewDidStartLoad:(UIWebView*)theWebView -{ - NSLog(@"Resetting plugins due to page load."); - [_commandQueue resetRequestId]; - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]]; -} - -/** - Called when the webview finishes loading. This stops the activity view. - */ -- (void)webViewDidFinishLoad:(UIWebView*)theWebView -{ - NSLog(@"Finished load of: %@", theWebView.request.URL); - // It's safe to release the lock even if this is just a sub-frame that's finished loading. - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; - - /* - * Hide the Top Activity THROBBER in the Battery Bar - */ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - [self processOpenUrl]; - - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:self.webView]]; -} - -- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error -{ - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; - - NSLog(@"Failed to load webpage with error: %@", [error localizedDescription]); -} - -- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType -{ - NSURL* url = [request URL]; - - /* - * Execute any commands queued with cordova.exec() on the JS side. - * The part of the URL after gap:// is irrelevant. - */ - if ([[url scheme] isEqualToString:@"gap"]) { - [_commandQueue fetchCommandsFromJs]; - return NO; - } - - /* - * If a URL is being loaded that's a file/http/https URL, just load it internally - */ - else if ([url isFileURL]) { - return YES; - } - - /* - * If we loaded the HTML from a string, we let the app handle it - */ - else if (self.loadFromString == YES) { - self.loadFromString = NO; - return YES; - } - - /* - * all tel: scheme urls we let the UIWebview handle it using the default behavior - */ - else if ([[url scheme] isEqualToString:@"tel"]) { - return YES; - } - - /* - * all about: scheme urls are not handled - */ - else if ([[url scheme] isEqualToString:@"about"]) { - return NO; - } - - /* - * all data: scheme urls are handled - */ - else if ([[url scheme] isEqualToString:@"data"]) { - return YES; - } - - /* - * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview. - */ - else { - if ([self.whitelist schemeIsAllowed:[url scheme]]) { - return [self.whitelist URLIsAllowed:url]; - } else { - if ([[UIApplication sharedApplication] canOpenURL:url]) { - [[UIApplication sharedApplication] openURL:url]; - } else { // handle any custom schemes to plugins - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; - } - } - - return NO; - } - - return YES; -} - -#pragma mark GapHelpers - -- (void)javascriptAlert:(NSString*)text -{ - NSString* jsString = [NSString stringWithFormat:@"alert('%@');", text]; - - [self.commandDelegate evalJs:jsString]; -} - -+ (NSString*)resolveImageResource:(NSString*)resource -{ - NSString* systemVersion = [[UIDevice currentDevice] systemVersion]; - BOOL isLessThaniOS4 = ([systemVersion compare:@"4.0" options:NSNumericSearch] == NSOrderedAscending); - - // the iPad image (nor retina) differentiation code was not in 3.x, and we have to explicitly set the path - if (isLessThaniOS4) { - if (CDV_IsIPad()) { - return [NSString stringWithFormat:@"%@~ipad.png", resource]; - } else { - return [NSString stringWithFormat:@"%@.png", resource]; - } - } - - return resource; -} - -+ (NSString*)applicationDocumentsDirectory -{ - NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString* basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; - - return basePath; -} - -#pragma mark CordovaCommands - -- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className -{ - if ([plugin respondsToSelector:@selector(setViewController:)]) { - [plugin setViewController:self]; - } - - if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { - [plugin setCommandDelegate:_commandDelegate]; - } - - [self.pluginObjects setObject:plugin forKey:className]; - [plugin pluginInitialize]; -} - -- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName -{ - if ([plugin respondsToSelector:@selector(setViewController:)]) { - [plugin setViewController:self]; - } - - if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { - [plugin setCommandDelegate:_commandDelegate]; - } - - NSString* className = NSStringFromClass([plugin class]); - [self.pluginObjects setObject:plugin forKey:className]; - [self.pluginsMap setValue:className forKey:[pluginName lowercaseString]]; - [plugin pluginInitialize]; -} - -/** - Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned. - */ -- (id)getCommandInstance:(NSString*)pluginName -{ - // first, we try to find the pluginName in the pluginsMap - // (acts as a whitelist as well) if it does not exist, we return nil - // NOTE: plugin names are matched as lowercase to avoid problems - however, a - // possible issue is there can be duplicates possible if you had: - // "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match - NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]]; - - if (className == nil) { - return nil; - } - - id obj = [self.pluginObjects objectForKey:className]; - if (!obj) { - obj = [[NSClassFromString(className)alloc] initWithWebView:webView]; - - if (obj != nil) { - [self registerPlugin:obj withClassName:className]; - } else { - NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); - } - } - return obj; -} - -#pragma mark - - -- (NSString*)appURLScheme -{ - NSString* URLScheme = nil; - - NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"]; - - if (URLTypes != nil) { - NSDictionary* dict = [URLTypes objectAtIndex:0]; - if (dict != nil) { - NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"]; - if (URLSchemes != nil) { - URLScheme = [URLSchemes objectAtIndex:0]; - } - } - } - - return URLScheme; -} - -/** - Returns the contents of the named plist bundle, loaded as a dictionary object - */ -+ (NSDictionary*)getBundlePlist:(NSString*)plistName -{ - NSString* errorDesc = nil; - NSPropertyListFormat format; - NSString* plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"]; - NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath]; - NSDictionary* temp = (NSDictionary*)[NSPropertyListSerialization - propertyListFromData:plistXML - mutabilityOption:NSPropertyListMutableContainersAndLeaves - format:&format errorDescription:&errorDesc]; - - return temp; -} - -#pragma mark - -#pragma mark UIApplicationDelegate impl - -/* - This method lets your application know that it is about to be terminated and purged from memory entirely - */ -- (void)onAppWillTerminate:(NSNotification*)notification -{ - // empty the tmp directory - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - NSError* __autoreleasing err = nil; - - // clear contents of NSTemporaryDirectory - NSString* tempDirectoryPath = NSTemporaryDirectory(); - NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath]; - NSString* fileName = nil; - BOOL result; - - while ((fileName = [directoryEnumerator nextObject])) { - NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName]; - result = [fileMgr removeItemAtPath:filePath error:&err]; - if (!result && err) { - NSLog(@"Failed to delete: %@ (error: %@)", filePath, err); - } - } -} - -/* - This method is called to let your application know that it is about to move from the active to inactive state. - You should use this method to pause ongoing tasks, disable timer, ... - */ -- (void)onAppWillResignActive:(NSNotification*)notification -{ - // NSLog(@"%@",@"applicationWillResignActive"); - [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO]; -} - -/* - In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state. - You can use this method to undo many of the changes you made to your application upon entering the background. - invariably followed by applicationDidBecomeActive - */ -- (void)onAppWillEnterForeground:(NSNotification*)notification -{ - // NSLog(@"%@",@"applicationWillEnterForeground"); - [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"]; -} - -// This method is called to let your application know that it moved from the inactive to active state. -- (void)onAppDidBecomeActive:(NSNotification*)notification -{ - // NSLog(@"%@",@"applicationDidBecomeActive"); - [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"]; -} - -/* - In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method - when the user quits an application that supports background execution. - */ -- (void)onAppDidEnterBackground:(NSNotification*)notification -{ - // NSLog(@"%@",@"applicationDidEnterBackground"); - [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO]; -} - -// /////////////////////// - -- (void)handleOpenURL:(NSNotification*)notification -{ - self.openURL = notification.object; -} - -- (void)processOpenUrl -{ - if (self.openURL) { - // calls into javascript global function 'handleOpenURL' - NSString* jsString = [NSString stringWithFormat:@"handleOpenURL(\"%@\");", [self.openURL description]]; - [self.webView stringByEvaluatingJavaScriptFromString:jsString]; - self.openURL = nil; - } -} - -// /////////////////////// - -- (void)dealloc -{ - [CDVURLProtocol unregisterViewController:self]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - self.webView.delegate = nil; - self.webView = nil; - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; - [_commandQueue dispose]; - [[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)]; -} - -@end |