// // RootViewController.m // Geopher Lite // // Created by Jeremy on 7/5/08. // Copyright Stone Software 2008. All rights reserved. // #import "RootViewController.h" #import "MainViewController.h" #import "FlipsideViewController.h" #import "FlipsideView.h" #import "textConverter.h" #import "LocatorModel.h" @implementation RootViewController @synthesize infoButton; @synthesize flipsideNavigationBar; @synthesize mainViewController; @synthesize flipsideViewController; @synthesize background; @synthesize redirect; - (void)viewDidLoad { MainViewController *viewController = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil]; self.mainViewController = viewController; viewController.rootView = self; [viewController release]; [self.view insertSubview:mainViewController.view belowSubview:infoButton]; // when loading, restore our data if (flipsideViewController == nil) [self loadFlipsideViewController]; if ([self loadPersistentData]) [[LocatorModel sharedInstance] setHasValidTarget:YES]; [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; // default the target screen to not go to sleep by default // load the system prefs [self loadSystemPreferences]; [self setBackground]; [(MainView*)mainViewController.view implementSkins:skinType]; [self setCompassTimer]; } - (void)timedUpdateBearing { // set the previous location as [[LocatorModel sharedInstance] setPreviousTimerLocation:[[LocatorModel sharedInstance] previousLocation]]; [self updateMainView:YES]; } - (void)setCompassTimer { // don't bother with this if we aren't rotationg the compass. if ([[NSUserDefaults standardUserDefaults] boolForKey:kCompassRotation] == NO) return; NSTimer* theTimer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:kTimerInterval target:self selector:@selector(timedUpdateBearing) userInfo:nil repeats:YES]; if ([theTimer isValid] == NO) NSLog(@"Invalid Timer!"); [[NSRunLoop currentRunLoop] addTimer:theTimer forMode:NSDefaultRunLoopMode]; [theTimer release]; } - (void)loadFlipsideViewController { FlipsideViewController *viewController = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil]; self.flipsideViewController = viewController; [viewController release]; [self.flipsideViewController.infoButton addTarget:self action:@selector(showSplash) forControlEvents:UIControlEventTouchUpInside]; [self.flipsideViewController.saveButton addTarget:self action:@selector(toggleView) forControlEvents:UIControlEventTouchUpInside]; // Set up the navigation bar UINavigationBar *aNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)]; aNavigationBar.barStyle = UIBarStyleBlackTranslucent; self.flipsideNavigationBar = aNavigationBar; [aNavigationBar release]; UIBarButtonItem* buttonItem = [[UIBarButtonItem alloc] initWithTitle:@"Return" style:UIBarButtonItemStylePlain target:self action:@selector(toggleView)]; UIBarButtonItem* buttonItem2 = [[UIBarButtonItem alloc] initWithTitle:@"More" style:UIBarButtonItemStylePlain target:self action:@selector(rotateActions)]; // UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(toggleView)]; // UIBarButtonItem *buttonItem2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(rotateActions)]; UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Geopher Lite"]; navigationItem.leftBarButtonItem = buttonItem; navigationItem.rightBarButtonItem = buttonItem2; [flipsideNavigationBar pushNavigationItem:navigationItem animated:YES]; [navigationItem release]; [buttonItem release]; [buttonItem2 release]; } - (IBAction)toggleView { /* This method is called when the info or Done button is pressed. It flips the displayed view from the main view to the flipside view and vice-versa. */ if (flipsideViewController == nil) { [self loadFlipsideViewController]; } UIView *mainView = mainViewController.view; UIView *flipsideView = flipsideViewController.view; if ([mainView superview] != nil) { if (redirect != nil) { // return to the application that called us via the redir URL. [[UIApplication sharedApplication] openURL: [NSURL URLWithString:redirect]]; } else { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.7]; [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES]; [flipsideViewController viewWillAppear:YES]; [mainViewController viewWillDisappear:YES]; [mainView removeFromSuperview]; [infoButton removeFromSuperview]; [self.view addSubview:flipsideView]; [self.view insertSubview:flipsideNavigationBar aboveSubview:flipsideView]; [mainViewController viewDidDisappear:YES]; [flipsideViewController viewDidAppear:YES]; [self flipsideToggleEvents:YES]; } } else { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.7]; [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.view cache:YES]; [mainViewController viewWillAppear:YES]; [flipsideViewController viewWillDisappear:YES]; [flipsideView removeFromSuperview]; [flipsideNavigationBar removeFromSuperview]; [self.view addSubview:mainView]; [self.view insertSubview:infoButton aboveSubview:mainViewController.view]; [flipsideViewController viewDidDisappear:YES]; [mainViewController viewDidAppear:YES]; [self flipsideToggleEvents:NO]; } [UIView commitAnimations]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview // Release anything that's not essential, such as cached data } - (void)dealloc { // save plist here [self savePersistentData]; [infoButton release]; [flipsideNavigationBar release]; [mainViewController release]; [flipsideViewController release]; [super dealloc]; } - (void) flipsideToggleEvents:(BOOL)toFlipside { if (toFlipside) { [[UIApplication sharedApplication] setIdleTimerDisabled:NO]; if ([[NSUserDefaults standardUserDefaults] stringForKey:kAlwaysOnGPSKey] == NO) { [[LocatorModel sharedInstance].locationManager stopUpdatingLocation]; [[LocatorModel sharedInstance].locationManager stopUpdatingHeading]; } } else { if ([[NSUserDefaults standardUserDefaults] stringForKey:kAlwaysOnGPSKey] == NO) { [[LocatorModel sharedInstance].locationManager startUpdatingLocation]; [[LocatorModel sharedInstance].locationManager startUpdatingHeading]; } [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; [mainViewController updateDisplay:YES]; [(MainView*)mainViewController.view implementSkins:skinType]; } } - (void) updateWaypoint:(NSString*)waypoint { if (flipsideViewController.updateWaypointString != nil) [flipsideViewController.updateWaypointString release]; [waypoint retain]; flipsideViewController.updateWaypointString = waypoint; } - (void) updateRedirect:(NSString*)redir { if (self.redirect != nil) [self.redirect release]; [redir retain]; self.redirect = redir; [infoButton setTitle:@"Return" forState:UIControlStateNormal]; [infoButton setTitle:@"Return" forState:UIControlStateHighlighted]; } - (void) updateMainView { [self updateMainView:NO]; } - (void) updateMainView:(BOOL)updateCompass { [mainViewController updateDisplay:updateCompass]; } #pragma mark - // load the persistent data off disk and return it as an NSDictionary - (NSDictionary*) readPersistentDataFile { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:kAppPlistFilename]; return [NSDictionary dictionaryWithContentsOfFile:finalPath]; } - (BOOL) writePersistentDataFile:(NSDictionary*)inDict { if (inDict == nil) return NO; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:kAppPlistFilename]; return [inDict writeToFile:finalPath atomically:YES]; } - (BOOL) setPersistentDataKey:(NSString*)inKey withValue:(id)inValue { NSDictionary* dict = [self readPersistentDataFile]; [dict setValue:inValue forKey:inKey]; return [self writePersistentDataFile:dict]; } // plist load functionality - (BOOL) loadPersistentData { BOOL retVal = NO; NSDictionary* outlineData = [[self readPersistentDataFile] retain]; // plist if (outlineData != nil) { if (redirect == nil) { double latitude; double longitude; // init the target coordinates NSString* str1 = [outlineData valueForKey:kTargetLatitudeKey]; NSString* str2 = [outlineData valueForKey:kTargetLongitudeKey]; if (str1 == nil) str1 = [[[NSString alloc] init] autorelease]; if (str2 == nil) str2 = [[[NSString alloc] init] autorelease]; textConverter* convLat = [[[textConverter alloc] initWithString:str1] autorelease]; textConverter* convLon = [[[textConverter alloc] initWithString:str2] autorelease]; [convLat decimalFromString:&latitude]; [convLon decimalFromString:&longitude]; CLLocation* loc = [[[CLLocation alloc] initWithLatitude:latitude longitude:longitude] autorelease]; [[LocatorModel sharedInstance] setTargetLocation: loc]; // now init the last current coordinates we knew about. str1 = [outlineData valueForKey:kCurrentLatitudeKey]; str2 = [outlineData valueForKey:kCurrentLongitudeKey]; if (str1 == nil) str1 = [[[NSString alloc] init] autorelease]; if (str2 == nil) str2 = [[[NSString alloc] init] autorelease]; convLat = [[[textConverter alloc] initWithString:str1] autorelease]; convLon = [[[textConverter alloc] initWithString:str2] autorelease]; [convLat decimalFromString:&latitude]; [convLon decimalFromString:&longitude]; loc = [[[CLLocation alloc] initWithLatitude:latitude longitude:longitude] autorelease]; [[LocatorModel sharedInstance] setCurrentLocation: loc]; } // update the waypoint text field self.flipsideViewController.updateWaypointString = [[outlineData valueForKey:kWaypointKey] retain]; // update the fields [[LocatorModel sharedInstance] update]; [outlineData release]; retVal = YES; } return retVal; } // plist save functionality - (void) savePersistentData { if (redirect != nil) // don't save coordinates if we are using the redirect information return; NSMutableDictionary* dict = [NSMutableDictionary dictionary]; // write the plist [dict setObject:[NSString stringWithFormat:@"%lu", kPListVersionNumber] forKey:kPListVersion]; [dict setObject:[[LocatorModel sharedInstance] targetLatitudeText: NO] forKey:kTargetLatitudeKey]; [dict setObject:[[LocatorModel sharedInstance] targetLongitudeText: NO] forKey:kTargetLongitudeKey]; [dict setObject:[[LocatorModel sharedInstance] currentLatitudeText: NO] forKey:kCurrentLatitudeKey]; [dict setObject:[[LocatorModel sharedInstance] currentLongitudeText: NO] forKey:kCurrentLongitudeKey]; if (flipsideViewController != nil) // this should never get called when this is nil, but just in case... [dict setObject:[NSString stringWithString:flipsideViewController.cacheIDTextView.text] forKey:kWaypointKey]; else NSLog(@"flipsieViewController nil in savePersistentData!"); // write the web archive // BOOL result = [NSKeyedArchiver archiveRootObject:((FlipsideView*)flipsideViewController.view).webView toFile:webArchive]; BOOL result = [flipsideViewController ArchiveWebView]; if (NO == result) { NSLog(@"Error writing web archive to file!"); } if (NO == [self writePersistentDataFile:dict]) { NSLog(@"Error writing plist to file!"); } } // system prefs - (void) loadSystemPreferences { NSString *testValue = [[NSUserDefaults standardUserDefaults] stringForKey:kPreferenceSkinKey]; if (testValue == nil) { // no default values have been set, create them here based on what's in our Settings bundle info // NSString *pathStr = [[NSBundle mainBundle] bundlePath]; NSString *settingsBundlePath = [pathStr stringByAppendingPathComponent:@"Settings.bundle"]; NSString *finalPath = [settingsBundlePath stringByAppendingPathComponent:@"Root.plist"]; NSDictionary *settingsDict = [NSDictionary dictionaryWithContentsOfFile:finalPath]; NSArray *prefSpecifierArray = [settingsDict objectForKey:@"PreferenceSpecifiers"]; NSNumber *skinDefault; NSDictionary *prefItem; for (prefItem in prefSpecifierArray) { NSString *keyValueStr = [prefItem objectForKey:@"Key"]; id defaultValue = [prefItem objectForKey:@"DefaultValue"]; if ([keyValueStr isEqualToString:kPreferenceSkinKey]) { skinDefault = defaultValue; } } // since no default values have been set (i.e. no preferences file created), create it here NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:1], kPreferenceSkinKey, [NSNumber numberWithInt:1], kMeasurementKey, nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; [[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kHideFoundCaches]; } if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:kAlwaysOnGPSKey]) // version 2 of prefs { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kCompassRotation]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAutoLoadSearchKey]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAlwaysOnGPSKey]; } if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:kUseInternalMapsKey]) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:kMagneticCompassKey]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kUseInternalMapsKey]; // update the following values for this version. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kCompassRotation]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAutoLoadSearchKey]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAlwaysOnGPSKey]; } // local prefs for one-time things or other local pref reasons. NSDictionary* localPrefs = [self readPersistentDataFile]; if ([localPrefs valueForKey:kAskedToTurnOnRotation] == nil) { // one time only set the compass rotation to on if it's off. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kCompassRotation]; } [self setPersistentDataKey:kAskedToTurnOnRotation withValue:[NSNumber numberWithBool:YES]]; // we're ready to do, so lastly set the key preference values skinType = [[NSUserDefaults standardUserDefaults] integerForKey:kPreferenceSkinKey]; } #pragma mark - - (void) moveButton:(UIButton*)button withOffsetX:(NSInteger)offsetX andOffsetY:(NSInteger)offsetY { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.0]; [UIView setAnimationDelay:0.0]; CGAffineTransform transform = CGAffineTransformMakeTranslation(offsetX, offsetY); // move it button.transform = transform; [UIView commitAnimations]; } // set the current skin's background and move the target Button as needed. - (void) setBackground { UIImage* image = nil; short change = 1; NSString* path = nil; NSBundle* bundle = [NSBundle mainBundle]; if (redirect != nil) // in all cases, set the target button to "Return" if we have a redirect value! { [infoButton setTitle:@"Return" forState:UIControlStateNormal]; [infoButton setTitle:@"Return" forState:UIControlStateHighlighted]; } // setup the image path based on the skin type. switch(skinType) { default: case 1: // default, do nothing, it should already be set up! change = 0; break; case 2: // modern look (black & white) path = [bundle pathForResource:kModernBackground ofType:kPNGExtension]; break; case 3: // copper look (pirate map) path = [bundle pathForResource:kCopperBackground ofType:kPNGExtension]; break; case 4: // green look (compass) path = [bundle pathForResource:kGreenBackground ofType:kPNGExtension]; break; case 5: change = 2; path = [bundle pathForResource:kOutdoorBackground ofType:kPNGExtension]; break; } if (change != 0) { image = [UIImage imageWithContentsOfFile:path]; if (image != nil) { [background setImage:image]; } } if (change == 1) { // resize the button infoButton.bounds = CGRectMake(infoButton.bounds.origin.x - 20, infoButton.bounds.origin.y, infoButton.bounds.size.width + 20, infoButton.bounds.size.height); if (redirect == nil) { [infoButton setTitle:@"Target" forState:UIControlStateNormal]; [infoButton setTitle:@"Target" forState:UIControlStateHighlighted]; } [infoButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; [infoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; [infoButton setAlpha:0.7]; [infoButton setOpaque:NO]; int x = -89; int y = 10; if (skinType == 4) { y += 6; } [self moveButton:infoButton withOffsetX:x andOffsetY:y]; } else if (change == 2) { // nothing here yet } } - (void) rotateActionsWithStage:(int)stage { switch (stage) { case latLonStage: case waypointStage: case browseButtonStage: [flipsideViewController hideControlsExcept:stage]; break; default: break; } } - (void) rotateActions { // auto-increment the which button stage is showing and then display them below. mButtonStage++; if (mButtonStage >= totalStages) mButtonStage = mButtonStage % totalStages; [self rotateActionsWithStage:mButtonStage]; } @end