From f9c1f51c0b59a082b39eaf8d7cb04ccf582178ff Mon Sep 17 00:00:00 2001 From: ganggangwang Date: Tue, 21 Apr 2015 14:18:29 +0800 Subject: [PATCH 1/9] intermidiate ( scan button instead of swipe getsture, display signal strength, add long-press menu) ps : not sure, because i've not real device yet. --- Mooshimeter.xcodeproj/project.pbxproj | 82 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 35 + .../xcschemes/Mooshimeter.xcscheme | 112 +++ Mooshimeter/AppDelegate.m | 49 +- .../checkicon.imageset/Contents.json | 22 + .../checkicon.imageset/check_icon.png | Bin 0 -> 3752 bytes .../checkicon.imageset/check_icon@2x.png | Bin 0 -> 4229 bytes .../menuicon.imageset/Contents.json | 21 + .../menuicon.imageset/menu_icon@2x 2.png | Bin 0 -> 206 bytes .../reloadicon.imageset/Contents.json | 21 + .../reloadicon.imageset/refresh_icon@2x.png | Bin 0 -> 945 bytes .../scanicon.imageset/Contents.json | 22 + .../scanicon.imageset/search_icon.png | Bin 0 -> 888 bytes .../scanicon.imageset/search_icon@2x.png | Bin 0 -> 1340 bytes .../settingicon.imageset/Contents.json | 21 + .../settingicon.imageset/settings@2x.png | Bin 0 -> 2217 bytes Mooshimeter/MeterViewController.h | 1 + Mooshimeter/MeterViewController.m | 116 +++ Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/OADProfile.h | 9 + Mooshimeter/OADProfile.m | 12 +- Mooshimeter/ScanViewController.m | 85 +- Mooshimeter/Vendor/KxMenu/KxMenu.h | 78 ++ Mooshimeter/Vendor/KxMenu/KxMenu.m | 872 ++++++++++++++++++ .../Vendor/KxMenu/UIImage+ImageEffects.h | 17 + .../Vendor/KxMenu/UIImage+ImageEffects.m | 185 ++++ .../Vendor/LDProgressView/LDProgressView.h | 29 + .../Vendor/LDProgressView/LDProgressView.m | 276 ++++++ .../Vendor/LDProgressView/UIColor+RGBValues.h | 23 + .../Vendor/LDProgressView/UIColor+RGBValues.m | 65 ++ .../SVProgressHUD.bundle/error.png | Bin 0 -> 371 bytes .../SVProgressHUD.bundle/error@2x.png | Bin 0 -> 739 bytes .../SVProgressHUD.bundle/success.png | Bin 0 -> 508 bytes .../SVProgressHUD.bundle/success@2x.png | Bin 0 -> 872 bytes .../Vendor/SVProgressHUD/SVProgressHUD.h | 44 + .../Vendor/SVProgressHUD/SVProgressHUD.m | 542 +++++++++++ .../self.iBytes += OAD_BLOC.textClipping | 0 Provisioning/Mooshi.p12 | Bin 0 -> 3238 bytes .../Mooshimeter_Development.mobileprovision | Bin 0 -> 9466 bytes ScanTableViewCell.m | 1 + 40 files changed, 2726 insertions(+), 16 deletions(-) create mode 100644 Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/Mooshimeter.xcscheme create mode 100644 Mooshimeter/Images.xcassets/checkicon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/checkicon.imageset/check_icon.png create mode 100644 Mooshimeter/Images.xcassets/checkicon.imageset/check_icon@2x.png create mode 100644 Mooshimeter/Images.xcassets/menuicon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/menuicon.imageset/menu_icon@2x 2.png create mode 100644 Mooshimeter/Images.xcassets/reloadicon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/reloadicon.imageset/refresh_icon@2x.png create mode 100644 Mooshimeter/Images.xcassets/scanicon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/scanicon.imageset/search_icon.png create mode 100644 Mooshimeter/Images.xcassets/scanicon.imageset/search_icon@2x.png create mode 100644 Mooshimeter/Images.xcassets/settingicon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/settingicon.imageset/settings@2x.png create mode 100644 Mooshimeter/Vendor/KxMenu/KxMenu.h create mode 100644 Mooshimeter/Vendor/KxMenu/KxMenu.m create mode 100644 Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.h create mode 100644 Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.m create mode 100755 Mooshimeter/Vendor/LDProgressView/LDProgressView.h create mode 100755 Mooshimeter/Vendor/LDProgressView/LDProgressView.m create mode 100755 Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.h create mode 100755 Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.m create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error.png create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error@2x.png create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/success.png create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/success@2x.png create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.h create mode 100644 Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.m create mode 100644 Mooshimeter/self.iBytes += OAD_BLOC.textClipping create mode 100755 Provisioning/Mooshi.p12 create mode 100755 Provisioning/Mooshimeter_Development.mobileprovision diff --git a/Mooshimeter.xcodeproj/project.pbxproj b/Mooshimeter.xcodeproj/project.pbxproj index 4373b14..4545467 100644 --- a/Mooshimeter.xcodeproj/project.pbxproj +++ b/Mooshimeter.xcodeproj/project.pbxproj @@ -46,6 +46,12 @@ 4ACF8AE11A0C918B00EC402B /* SmartNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ACF8AE01A0C918B00EC402B /* SmartNavigationController.m */; }; 4ADDC5D51A882CFB00FC5093 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ADDC5D41A882CFB00FC5093 /* Accelerate.framework */; }; 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF4D8DC1A2BCD2600622F58 /* MeterSettingsView.m */; }; + 842D9F641AE423B300ABBB89 /* LDProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F631AE423B200ABBB89 /* LDProgressView.m */; }; + 842D9F671AE424ED00ABBB89 /* UIColor+RGBValues.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */; }; + 847121951AE61AC400035BB2 /* SVProgressHUD.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 847121921AE61AC400035BB2 /* SVProgressHUD.bundle */; }; + 847121961AE61AC400035BB2 /* SVProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 847121941AE61AC400035BB2 /* SVProgressHUD.m */; }; + 847A13A11AE3B94900E36E17 /* KxMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 847A139E1AE3B94900E36E17 /* KxMenu.m */; }; + 847A13A21AE3B94900E36E17 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = 847A13A01AE3B94900E36E17 /* UIImage+ImageEffects.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -205,6 +211,17 @@ 4ADDC5D41A882CFB00FC5093 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 4AF4D8DB1A2BCD2600622F58 /* MeterSettingsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeterSettingsView.h; sourceTree = ""; }; 4AF4D8DC1A2BCD2600622F58 /* MeterSettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeterSettingsView.m; sourceTree = ""; }; + 842D9F621AE423B200ABBB89 /* LDProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LDProgressView.h; sourceTree = ""; }; + 842D9F631AE423B200ABBB89 /* LDProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDProgressView.m; sourceTree = ""; }; + 842D9F651AE424ED00ABBB89 /* UIColor+RGBValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+RGBValues.h"; sourceTree = ""; }; + 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+RGBValues.m"; sourceTree = ""; }; + 847121921AE61AC400035BB2 /* SVProgressHUD.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SVProgressHUD.bundle; sourceTree = ""; }; + 847121931AE61AC400035BB2 /* SVProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVProgressHUD.h; sourceTree = ""; }; + 847121941AE61AC400035BB2 /* SVProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SVProgressHUD.m; sourceTree = ""; }; + 847A139D1AE3B94900E36E17 /* KxMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KxMenu.h; sourceTree = ""; }; + 847A139E1AE3B94900E36E17 /* KxMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KxMenu.m; sourceTree = ""; }; + 847A139F1AE3B94900E36E17 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ImageEffects.h"; sourceTree = ""; }; + 847A13A01AE3B94900E36E17 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ImageEffects.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -237,6 +254,7 @@ 0C63852017D694DC00D3FB31 = { isa = PBXGroup; children = ( + 847A13941AE3B27800E36E17 /* Vendor */, 4A6B13C91A2EA97C000AF17B /* Default-568h@2x.png */, 4AA9EA1E1A12B53F0031C12B /* LGBluetooth */, 4ABE54171A080A8300277456 /* OAD */, @@ -515,6 +533,51 @@ name = GraphSettingsView; sourceTree = ""; }; + 842D9F611AE423B200ABBB89 /* LDProgressView */ = { + isa = PBXGroup; + children = ( + 842D9F651AE424ED00ABBB89 /* UIColor+RGBValues.h */, + 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */, + 842D9F621AE423B200ABBB89 /* LDProgressView.h */, + 842D9F631AE423B200ABBB89 /* LDProgressView.m */, + ); + name = LDProgressView; + path = Mooshimeter/Vendor/LDProgressView; + sourceTree = SOURCE_ROOT; + }; + 847121911AE61AC400035BB2 /* SVProgressHUD */ = { + isa = PBXGroup; + children = ( + 847121921AE61AC400035BB2 /* SVProgressHUD.bundle */, + 847121931AE61AC400035BB2 /* SVProgressHUD.h */, + 847121941AE61AC400035BB2 /* SVProgressHUD.m */, + ); + name = SVProgressHUD; + path = Mooshimeter/Vendor/SVProgressHUD; + sourceTree = SOURCE_ROOT; + }; + 847A13941AE3B27800E36E17 /* Vendor */ = { + isa = PBXGroup; + children = ( + 847121911AE61AC400035BB2 /* SVProgressHUD */, + 842D9F611AE423B200ABBB89 /* LDProgressView */, + 847A139C1AE3B94900E36E17 /* KxMenu */, + ); + path = Vendor; + sourceTree = ""; + }; + 847A139C1AE3B94900E36E17 /* KxMenu */ = { + isa = PBXGroup; + children = ( + 847A139D1AE3B94900E36E17 /* KxMenu.h */, + 847A139E1AE3B94900E36E17 /* KxMenu.m */, + 847A139F1AE3B94900E36E17 /* UIImage+ImageEffects.h */, + 847A13A01AE3B94900E36E17 /* UIImage+ImageEffects.m */, + ); + name = KxMenu; + path = Mooshimeter/Vendor/KxMenu; + sourceTree = SOURCE_ROOT; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -565,11 +628,6 @@ LastTestingUpgradeCheck = 0610; LastUpgradeCheck = 0610; ORGANIZATIONNAME = mooshim; - TargetAttributes = { - 0C63852817D694DC00D3FB31 = { - DevelopmentTeam = S9QZRBTSZT; - }; - }; }; buildConfigurationList = 0C63852417D694DC00D3FB31 /* Build configuration list for PBXProject "Mooshimeter" */; compatibilityVersion = "Xcode 3.2"; @@ -594,6 +652,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 847121951AE61AC400035BB2 /* SVProgressHUD.bundle in Resources */, 0C63853717D694DC00D3FB31 /* InfoPlist.strings in Resources */, 4A6B13CC1A2FE808000AF17B /* Images.xcassets in Resources */, 0C349D731885252E00B6D64F /* Default.png in Resources */, @@ -652,13 +711,16 @@ 4AA9EA2D1A12B54E0031C12B /* LGCentralManager.m in Sources */, 0C63853917D694DC00D3FB31 /* main.m in Sources */, 4A777EA71A09B2F2007FEEB9 /* ScanTableViewCell.m in Sources */, + 847121961AE61AC400035BB2 /* SVProgressHUD.m in Sources */, 4AA9EA2C1A12B54E0031C12B /* CBUUID+StringExtraction.m in Sources */, 4ABE541E1A080A9000277456 /* OADProfile.m in Sources */, 4A5F55581AB784B100212D1D /* MooshimeterDeviceSimulator.m in Sources */, + 847A13A21AE3B94900E36E17 /* UIImage+ImageEffects.m in Sources */, 4ACF8AE11A0C918B00EC402B /* SmartNavigationController.m in Sources */, 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */, 0C63853D17D694DC00D3FB31 /* AppDelegate.m in Sources */, 0C63854C17D694DC00D3FB31 /* ScanViewController.m in Sources */, + 842D9F641AE423B300ABBB89 /* LDProgressView.m in Sources */, 4AA9EA2F1A12B54E0031C12B /* LGPeripheral.m in Sources */, 4AA9EA2E1A12B54E0031C12B /* LGCharacteristic.m in Sources */, 0C63854F17D694DC00D3FB31 /* MeterViewController.m in Sources */, @@ -666,7 +728,9 @@ 0C78962417D7E56B00D1439E /* BLEUtility.m in Sources */, 4ABE54201A080A9000277456 /* OADProgressViewController.m in Sources */, 4AAB4A261A1052C900794EC8 /* ChannelView.m in Sources */, + 842D9F671AE424ED00ABBB89 /* UIColor+RGBValues.m in Sources */, 4AC27CC31A19C0C3008CF592 /* GraphSettingsView.m in Sources */, + 847A13A11AE3B94900E36E17 /* KxMenu.m in Sources */, 0C78967717DE74B800D1439E /* MooshimeterDevice.m in Sources */, 0C78972317E378FE00D1439E /* GraphViewController.m in Sources */, ); @@ -782,6 +846,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -800,8 +865,8 @@ "-all_load", ); PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + PROVISIONING_PROFILE = "0b2a1bee-e707-40a3-b180-a4b90b179733"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "0b2a1bee-e707-40a3-b180-a4b90b179733"; SDKROOT = iphoneos; SEPARATE_STRIP = NO; TARGETED_DEVICE_FAMILY = "1,2"; @@ -814,6 +879,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -831,7 +897,7 @@ "-all_load", ); PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE = "0b2a1bee-e707-40a3-b180-a4b90b179733"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; SEPARATE_STRIP = NO; diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..5fcde47 --- /dev/null +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/Mooshimeter.xcscheme b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/Mooshimeter.xcscheme new file mode 100644 index 0000000..fb3a6ab --- /dev/null +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/Mooshimeter.xcscheme @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/AppDelegate.m b/Mooshimeter/AppDelegate.m index 338aa17..34f465a 100644 --- a/Mooshimeter/AppDelegate.m +++ b/Mooshimeter/AppDelegate.m @@ -17,6 +17,9 @@ ***************************/ #import "AppDelegate.h" +#import "Vendor/SVProgressHUD/SVProgressHUD.h" + +#define SHOW_WAIT_DIALOG(MESSAGE) [SVProgressHUD showWithStatus:MESSAGE maskType:SVProgressHUDMaskTypeClear] @implementation AppDelegate @@ -64,8 +67,15 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( b = [UIButton buttonWithType:UIButtonTypeSystem]; b.userInteractionEnabled = YES; [b addTarget:self action:@selector(settings_button_press) forControlEvents:UIControlEventTouchUpInside]; + +#if 1 // Uncomment if needs classic button [b.titleLabel setFont:[UIFont systemFontOfSize:24]]; [b setTitle:@"\u2699" forState:UIControlStateNormal]; +#else + [b setFrame:CGRectMake(0, 0, 44, 30)]; + [b setImage:[UIImage imageNamed:@"common_setting"] forState:UIControlStateNormal]; +#endif + [b setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [[b layer] setBorderWidth:2]; [[b layer] setBorderColor:[UIColor darkGrayColor].CGColor]; @@ -87,6 +97,8 @@ - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + + } - (void)applicationDidEnterBackground:(UIApplication *)application @@ -140,8 +152,9 @@ - (void)scanForMeters NSTimer* refresh_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.scan_vc selector:@selector(reloadData) userInfo:nil repeats:YES]; //self.scan_vc.title = @"Scan in progress..."; - self.nav.navigationItem.title = @"Scan in progress..."; + // Uncommented by Jianying Shi + // self.nav.navigationItem.title = @"Scan in progress..."; [c scanForPeripheralsByInterval:5 services:services options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES} @@ -150,7 +163,9 @@ - (void)scanForMeters [refresh_timer invalidate]; [self.scan_vc.refreshControl endRefreshing]; [self.scan_vc reloadData]; - self.nav.navigationItem.title = @"Pull down to scan"; + + // Uncommented by Jianying Shi + // self.nav.navigationItem.title = @"Pull down to scan"; }]; } @@ -203,6 +218,36 @@ -(void)switchToGraphView:(UIDeviceOrientation)new_o { [self.nav pushViewController:self.graph_vc animated:YES]; } } +-(void)updateFirmwareIfNeeded +{ + if( g_meter->oad_mode ) { + // We connected to a meter in OAD mode as requested previously. Update firmware. + NSLog(@"Connected in OAD mode"); + if( YES || [g_meter getAdvertisedBuildTime] != self.oad_profile->imageHeader.build_time ) { + NSLog(@"Starting upload"); + + // Add by Jianying Shi + // Display updating state. + [SVProgressHUD showWithStatus:@"Updating firmware..." maskType : SVProgressHUDMaskTypeClear]; + [self.oad_profile setCompletionBlock:^(NSError* error) { + [SVProgressHUD dismiss]; + }]; + + [self.oad_profile startUpload]; + } else { + NSLog(@"We connected to an up-to-date meter in OAD mode. Disconnecting."); + [g_meter.p disconnectWithCompletion:nil]; + } + } + else if( [g_meter getAdvertisedBuildTime] < self.oad_profile->imageHeader.build_time ) { + // Require a firmware update! + NSLog(@"FIRMWARE UPDATE REQUIRED."); + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Firmware Update" message:@"This meter requires a firmware update. This will take about a minute. Upgrade now?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Upgrade Now", nil]; + [alert show]; + } + + +} #pragma mark ScatterViewControllerDelegate diff --git a/Mooshimeter/Images.xcassets/checkicon.imageset/Contents.json b/Mooshimeter/Images.xcassets/checkicon.imageset/Contents.json new file mode 100644 index 0000000..7a73488 --- /dev/null +++ b/Mooshimeter/Images.xcassets/checkicon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "check_icon.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "check_icon@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/checkicon.imageset/check_icon.png b/Mooshimeter/Images.xcassets/checkicon.imageset/check_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..46728d1e0e045742d161d46e45f8c08691a73aa9 GIT binary patch literal 3752 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 TR+%3<00000NkvXXu0mjfUYwgW literal 0 HcmV?d00001 diff --git a/Mooshimeter/Images.xcassets/scanicon.imageset/Contents.json b/Mooshimeter/Images.xcassets/scanicon.imageset/Contents.json new file mode 100644 index 0000000..faf4e4d --- /dev/null +++ b/Mooshimeter/Images.xcassets/scanicon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "search_icon.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "search_icon@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/scanicon.imageset/search_icon.png b/Mooshimeter/Images.xcassets/scanicon.imageset/search_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b88ab729ac4d079b2114db87b1f74c6ee83a59b7 GIT binary patch literal 888 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XY(R<}eL_MK(-}fCGK&>}s3ap*Au~BYPa&-+KUcv$HLoNm zH7zkYRWBLHO)E-;$QCOUrmYJ4} zXE@IoSwCajkUr5(;fQ_QNA~AaL*1sO<~T1~qP~4seC@7Xt8_fgPyDP2Ye;|4|5Wge zt*_nzj|5ni#tNm;6SL1t!3B8!w&-vFMoMUXp_$V%i;lrjrl*qVxQYO^dEnm zw&LU6yyb7dSD$Weljr}fb)mH4g0a%lwB2{h7`g*{54_*?<6!=#xb<7lrX?HB3S?sP zFD+S+U&E=Sygf9e?MOxEBZY|`90k!!oPTW;V4q%UG*cu{o3DyFU6y<0??u6;=HI$I zULELk*?aL|tNb^%rb{jnYs0om%lt{O6@0dUchgD!Z^auUG!l%pHSyO;aetY-Kv=RG5)wNvi?Bf%F*``I+~=2^`?JIf+_ z*Qs6e{FeLPk2IO-BQnqS%1*Us=gPa}vd=n;*0D0w*-k#WCFAeaY74o3QHj?HFSq<; z?oE92(qz_+lg#rgFMnmUN!@(&{=Um4CQ_Oo?tSc>vSjZ0=kqlf_RTc^RHirmb>&pO zS_2tAQ@_JoXKkO&?v}x_ebKZvwao_;78E|&C)%`my3@iBHRt=i#GC@GntHBxA5HS! z6`b^E!S$@&>sGzGd+cqXWAn+--Fz!NzXYxP5`Qe0J>`UL@eby%MLYi});8Vfzw`dP osh{Z6nDrrAwl60?na{w?usZNz``IZww}aA?r>mdKI;Vst0K4URMgRZ+ literal 0 HcmV?d00001 diff --git a/Mooshimeter/Images.xcassets/scanicon.imageset/search_icon@2x.png b/Mooshimeter/Images.xcassets/scanicon.imageset/search_icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4c02ef9bdfeaa6152c37cf3e7575cab16139c6 GIT binary patch literal 1340 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GHXucgJ|Q8A=?ozmnZ*h~RFaXZkeQsHr;t{ZpR3@WnpcvO znwFTHs+SDprWK_^WQ!GwQ%m#|Ze+hW0yIY+WR7!wL1j^9dPWIDkbfl5&`1ThAXirf zA5UjjzhGB|dpEl?fl6I7Ln2CCQj3#|G7CyF^Yeg04CL9pNAQOfP_Ht`K!n{O$(g%)6Ht)4t)PmtIEaZWa7a0ppBt!`B$Gm$B(;YUMJK^uM<-_;y6MD;rxUI1=WW8UuMd4J}CdiqVhohBTsmD&jOb9#&(zV!`bG|o43J6>qz{} zg9i^TS|P-0KZ9}dkJswqYzdjs%o=hcu3hfVc287m)_!E$;5FghiHN5YrhnLaD%6Re zpWn=6t^Vsz7cK<&OibzT?_Z~`@y>Hi}iNe-|Wo?B9RA zW7eJNG6(iuPk+JC*4{3w)R20TQHPuR!10v%OF|M=L*iL&lqXG_)41jM{KL`>Pkl3& z21QO*l&dIRl%ji&;Ty*#UeCqd@P_x;w?2)J|;| zmy(n`SZe9t6ZY=eGq)2hZgNkQ|D9U=-;QzR-A~KJtW~UUbQ?^5@xyfgoz()jZrqsR z%-X1Z?CaO9oE%d*|L_Gc%~Mg8`^&X=>W*Ezw0_1cU7(y?Q?us=qxiL&zSK{*J^6P8 z-Elv4`n0#r^mPnnYHDhq9*A6y;mSxm`fE4ioE85qRnQ z`M``hbIvq3sH~3MdUf5UYJHZPwX+i5#&NJ6;HuqNT36TC$909HamAG@SEjHY^xQqO z*^#T>xc$-17i!IJ9L)*u(`)(u%)KR6koNaqj&{T5&6~p>&2IMa_4l`aW8KTTuw9_9 zICb?oQLBc_moJySx0e2`eCEiRGd@?187v&bZz^Qitro4@&3=RRVXkZ+6SIY-DQzzu!ws~aS>9T0mmkWOH@%%tuUpGUT0g5Bj54`%z4~G tj@Iv<%CK+;ufZ&-DF^4dUi-&wz#!AiWxv#Cjwq;1@O1TaS?83{1ON>gM|c1L literal 0 HcmV?d00001 diff --git a/Mooshimeter/Images.xcassets/settingicon.imageset/Contents.json b/Mooshimeter/Images.xcassets/settingicon.imageset/Contents.json new file mode 100644 index 0000000..da26ae0 --- /dev/null +++ b/Mooshimeter/Images.xcassets/settingicon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "settings@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/settingicon.imageset/settings@2x.png b/Mooshimeter/Images.xcassets/settingicon.imageset/settings@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d099e64ebac766125b65df61329d28db951fe1d8 GIT binary patch literal 2217 zcmaJ@eLPfY8y=;Mp+s!T#uy(;Ag_cya^%J=Y<& zAYd@qI&Uv_uzDqGKKNSo-`wp`oqE|U=S0XupjdedAO&GeJ`@8Yy+uGA7z_gZ)Wj~( z1qNGfEeMT}NBD7RJV=BEG(K2`NTNo=U@mS73BVJAa%2n`ClE7G&s%;(Aq9K}D%{Zz z?+EZmvid1x-yL0_6ZwAxaR-XbJ}EJugkY*O+lAbPu5* z?fd|qK&&y?zeo7_(Y(bnIUweN-fRX+&4CpN_%sfIMJ0RSotzx;ECPW;W|25dcRZVk z=eRqwsT9&OmJRWeM4(u{jOG7>rLKt8h(RP#M`nXkK{CkqkU}El`=V)rm2+`ik?#YR zzj7{=6|p#VGC0j>|7+CCCbfPv+mFIkA3jPS6sz?vRSWx0%-9VWY>k;Wn;EJY8r$jT z6&hrm5#a0VMA`iM``u*Idj^(G@n3QF>eb~qU`+D__DmLslg%kWJgwe;UweAns_ghk zI3{yR=0Fntg37I^h<2z|%|2>L`e9^lG~KPk`tx}OX|D8(X~x`>(%IYTIffY}J_;YN z;a`gg-ZzLkHrlZVM+E+BjjK9m*8({QEPIN$ww$%_=hpiBE|R12xK_r(m)I+^K;~@q zbuKF-BJbuEt#l@}<`)%Fn^WVuzM^~B)Llu``*t>K$kXolJOZA`!abzVPuP!PV4_v6 ziRZ^b?8EB5C)+oGKNxir+{(@OSKU=eF`A>SGQJRKEMtwM-|- zV+Sg40dZw3K@m-q8A{*+pTf91b)0iUMr8_+{r0dpJc!fJL zPo@wA$7%3XBa3iVS;1*xdFQk9w3%9K@+9JJajOR_e|3OaRMVc%+gk?9YhF^7J6T3i zGmH~Y=PG?O5_!q zai7i?b;KRgy4+baz=Jkv1@+mqTg~fOcAO~%^E?-)NAhbr!Vt|#9+&qm<@KDTR#qR= z84Ztrv!;G5xMLyOVr+xQU^^6OXIc8dq()g5_T#F}4@}a@^{dTwE4Nr<}*1bOpeD`}udoPQcCfXGivXhW)!Z`%#8! z&8tgd*S0jq2$SkP*?yvwdi}m-KXK!UxbL-(9{UY$vwiE7c6wvJ<3?3e(cN%D)(qTr zS{tBWT@s?3Z4e6mSb+s5AB{$@p^h2AAP4vIgpj?$!r)+r2ML%1l){S+?x?^Y)I#roCkWQ$YXu`QzDp$ z>j$}~6gxWktsKMxHQr`Ry-yqcjM5~v^nP^fzeX*4u|5kJ=Foz_PI_BIMEMwL@l1b# zmEt>l+scTkpBqp`k@Ns6YmjXhK6CV3Ld7oZBr&2N4a)MKO9^Oy0p;$x^*7o|)SD*_PQaR5`hK*FDyI_uD4)i)wUzZdAie z^)PJcn(}P(Fmh7INP3U!+PSz5*m|RJLvPVf@*cs=B@fl8Y_j)SX?~Ourck%p#ri@m zWuP@D>|o?Y#)82~E_yJMadNn_cs$a8I#bIUwRe~mcJ2JVO;(VaQY+k!HapvF_E1pK zkf8eMLS~udK6&Z#j>+JY>GKy@2wvZ|sbz!BSRv+C*O(kr%van}0J^Yf|4|#|%(*Z}+ME+Pk4IgIzcB$je6ev#P1{pPq+u z-hz|*e;5vdcHt@uQe#NgUii0XgL)`tx4-#M>oa!QTfnZpHg7s9++b~sCzFoY*{G`7 zGeY$rXo?sfKW+Qm-34tzeo?;NC!|08kF9AhnWqln%s-*G9yKs6RPx73r?_9&>`}Z9 b*{22TE)?|Tyf%o}TpqkR{_HcX-I;#@&6JIj literal 0 HcmV?d00001 diff --git a/Mooshimeter/MeterViewController.h b/Mooshimeter/MeterViewController.h index 9ff076f..a7768ad 100644 --- a/Mooshimeter/MeterViewController.h +++ b/Mooshimeter/MeterViewController.h @@ -28,6 +28,7 @@ along with this program. If not, see . @protocol MeterViewControllerDelegate @required -(void)switchToGraphView:(UIDeviceOrientation)new_o; +-(void)updateFirmwareIfNeeded; @end @interface MeterViewController : UIViewController diff --git a/Mooshimeter/MeterViewController.m b/Mooshimeter/MeterViewController.m index 1da51d2..2443b43 100644 --- a/Mooshimeter/MeterViewController.m +++ b/Mooshimeter/MeterViewController.m @@ -18,8 +18,17 @@ #import "MeterViewController.h" +#import "Vendor/KxMenu/KxMenu.h" + dispatch_semaphore_t tmp_sem; +#define kContextMenu_Tag 2000 + +@interface MeterViewController () { + KxMenu* settingsMenu; +} +@end + @implementation MeterViewController -(BOOL)prefersStatusBarHidden { return YES; } @@ -77,8 +86,115 @@ - (void)viewDidLoad [v addSubview:sv]; [self.view addSubview:v]; + + // By Jianying Shi + // -- Scan settings button + UIButton *menu_button = [UIButton buttonWithType:UIButtonTypeSystem]; + menu_button.frame = CGRectMake(0.0, 0.0, 44, 30); + // [setting_button setTitle:@"Setting" forState : UIControlStateNormal]; + [menu_button setImage:[UIImage imageNamed:@"menuicon"] forState:UIControlStateNormal]; + + UILongPressGestureRecognizer *longpressScanSetting = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longpressScanSetting:)]; + [menu_button addGestureRecognizer:longpressScanSetting]; + + UIBarButtonItem* nav_menu_button = [[UIBarButtonItem alloc] initWithCustomView:menu_button]; + + if( self.navigationItem ) + self.navigationItem.leftBarButtonItem = nav_menu_button; +} + +#pragma mark - Menu Item Press Handler + +- (void) longpressScanSetting : (UIBarButtonItem*) sender +{ + // Check if menu is currently showing. + UIView* existMenu = [self.view viewWithTag:kContextMenu_Tag]; + if( existMenu != nil ) + return; + + if (!settingsMenu) { + + settingsMenu = [KxMenu new]; + settingsMenu.menuItems = self.createSettingsMenuItems; + settingsMenu.blurredBackground = NO; + + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { + settingsMenu.blurredBackground = YES; + } + + settingsMenu.tintColor = [UIColor darkGrayColor];//[[UIColor whiteColor] colorWithAlphaComponent:0.5f]; + settingsMenu.tintColor1 = [UIColor lightGrayColor]; + + settingsMenu.selectedColor = [UIColor colorWithRed:0.9f green:0 blue:0 alpha:1.f]; + settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; + } + + // Display check ID + NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; + NSString* deviceUUID = [g_meter.p UUIDString]; + + KxMenuItem* checkItem = settingsMenu.menuItems[2]; + if( savedUUID == nil || [savedUUID isEqualToString:deviceUUID] == NO ) + { + checkItem.image = nil; + } + else{ + checkItem.image = [UIImage imageNamed:@"checkicon"]; + } + + UIView* contentView = (UIView*) [sender valueForKey:@"view"]; + CGRect buttonRect = contentView.frame; + buttonRect.origin.y += buttonRect.size.height; + + [settingsMenu setTag:kContextMenu_Tag]; + [settingsMenu showMenuInView:self.view + fromRect:buttonRect]; +} + +- (void) firmwareUpdatePressed : (id) sender +{ + NSLog(@"Firmware Update"); + + [self.delegate updateFirmwareIfNeeded]; +} + +- (void) autoConnectPressed : (id) sender +{ + NSLog(@"Auto connect"); + + NSString* deviceUUID = [g_meter.p UUIDString]; + if( deviceUUID ) + [[NSUserDefaults standardUserDefaults] setObject:deviceUUID forKey:@"autoUUID"]; +} + +- (NSArray*) createSettingsMenuItems { + NSArray *menuItems = + @[ + + [KxMenuItem menuItem:@"Setting" + image:nil + target:nil + action:NULL], + + [KxMenuItem menuItem:@"Firmware Update" + image:nil + target:self + action:@selector(firmwareUpdatePressed:)], + + [KxMenuItem menuItem:@"Auto Connect" + image:[UIImage imageNamed:@"checkicon"] + target:self + action:@selector(autoConnectPressed:)], + ]; + + KxMenuItem *first = menuItems[0]; + first.foreColor = [UIColor colorWithRed:47/255.0f green:112/255.0f blue:225/255.0f alpha:1.0]; + first.alignment = NSTextAlignmentCenter; + + return menuItems; } +#pragma mark - View lifey cycle -(void) viewWillDisappear:(BOOL)animated { [self pause]; } diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index 9ca5130..3b9607c 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 173 + 325 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/OADProfile.h b/Mooshimeter/OADProfile.h index a772db6..ec0ef63 100644 --- a/Mooshimeter/OADProfile.h +++ b/Mooshimeter/OADProfile.h @@ -13,9 +13,14 @@ @class BLETIOADProgressViewController; +typedef void (^FirmwareUpdateFinishCallback)(NSError *error); + @interface OADProfile : NSObject { @public img_hdr_t imageHeader; + + // Add by Jianying Shi. + FirmwareUpdateFinishCallback aCompletionBlock; } @property (strong,nonatomic) NSData *imageData; @@ -36,6 +41,10 @@ -(instancetype) init:(NSString*) filename; +// Add by Jianying Shi. +// Download new Firmware +-(void) setCompletionBlock : (FirmwareUpdateFinishCallback) aCallback; + -(void) startUpload; -(void) completionDialog; diff --git a/Mooshimeter/OADProfile.m b/Mooshimeter/OADProfile.m index f16397d..db89651 100644 --- a/Mooshimeter/OADProfile.m +++ b/Mooshimeter/OADProfile.m @@ -157,6 +157,10 @@ -(void)sendNextBlock { self.inProgramming = NO; dispatch_queue_t mq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(mq, ^{ + + // Call completion block here! + aCompletionBlock(nil); + [self completionDialog]; }); return; @@ -187,9 +191,11 @@ -(void) completionDialog { complete = [[UIAlertView alloc]initWithTitle:@"Firmware upgrade complete" message:@"Firmware upgrade was successfully completed, device needs to be reconnected" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [complete show]; } -@end - - +-(void) setCompletionBlock : (FirmwareUpdateFinishCallback) aCallback; +{ + aCompletionBlock = aCallback; +} +@end diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 00c175d..6628f02 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -19,11 +19,19 @@ #import "ScanViewController.h" #import "meterViewController.h" +#import "Vendor/KxMenu/KxMenu.h" +#import "Vendor/LDProgressView/LDProgressView.h" + // Uncomment if you want a simulated meter to appear in the scan list #define SIMULATED_METER +#define kRSSIView_Tag 1000 +#define kContextMenu_Tag 2000 + @interface ScanViewController () { NSMutableArray *_objects; + + KxMenu* settingsMenu; } @end @@ -46,23 +54,75 @@ -(void)reloadData { LGCentralManager* c = [LGCentralManager sharedInstance]; self.peripherals = [c.peripherals copy]; [self.tableView reloadData]; + + // Auto - connect logics. + NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; + if( savedUUID != nil && savedUUID.length > 0 ) + { + for( NSInteger i = 0; i < self.peripherals.count; i ++ ) + { + LGPeripheral* p = self.peripherals[i]; + if( [[p UUIDString] isEqualToString:savedUUID] == YES ) + { + [self.delegate handleScanViewSelect:p]; + break; + } + } + } } +// by Jianying Shi. +// Hanlder of Menu Setting +#pragma mark - View lifecycle + - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[ScanTableViewCell class] forCellReuseIdentifier:@"Cell"]; +#if 0 NSLog(@"Creating refresh handler..."); UIRefreshControl *rescan_control = [[UIRefreshControl alloc] init]; [rescan_control addTarget:self.delegate action:@selector(handleScanViewRefreshRequest) forControlEvents:UIControlEventValueChanged]; self.refreshControl = rescan_control; +#endif + + // Added by Jianying Shi + // 04/19/2015 + + // -- Scan Mooshimeter button + // UIBarButtonItem* nav_scan_btn = [[UIBarButtonItem alloc] initWithTitle:@"Scan" style:UIBarButtonItemStylePlain target:self.delegate action:@selector(handleScanViewRefreshRequest)]; + UIBarButtonItem* nav_scan_btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"reloadicon"] style:UIBarButtonItemStyleBordered target:self.delegate action:@selector(handleScanViewRefreshRequest)]; + +#if 0 + // -- Scan settings button + UIButton *menu_button = [UIButton buttonWithType:UIButtonTypeSystem]; + menu_button.frame = CGRectMake(0.0, 0.0, 44, 30); + // [setting_button setTitle:@"Setting" forState : UIControlStateNormal]; + [menu_button setImage:[UIImage imageNamed:@"menuicon"] forState:UIControlStateNormal]; + + UILongPressGestureRecognizer *longpressScanSetting = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longpressScanSetting:)]; + [menu_button addGestureRecognizer:longpressScanSetting]; + + UIBarButtonItem* nav_menu_button = [[UIBarButtonItem alloc] initWithCustomView:menu_button]; + + NSArray* navButtonArray = [[NSArray alloc] initWithObjects:nav_scan_btn, nav_menu_button, nil]; + + if( self.navigationItem != nil ) + self.navigationItem.leftBarButtonItems = navButtonArray; +#endif + + if( self.navigationItem != nil ) + self.navigationItem.leftBarButtonItem = nav_scan_btn; } +#pragma mark - View lifecycle -(void)viewDidAppear:(BOOL)animated { - [self setTitle:@"Swipe down to scan"]; + // [self setTitle:@"Swipe down to scan"] + // [self setTitle:@"Tap Scan Button"]; + if(g_meter) { // If we've appeared, disconnect whatever we were talking to. [g_meter disconnect:nil]; @@ -126,8 +186,29 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N ScanTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; + // Add progress view to show signal strength + // By Jianying Shi. 04/19/2015 + // Detect if progress view has already created. + LDProgressView* viewForCell = (LDProgressView*) [cell viewWithTag:indexPath.row + kRSSIView_Tag]; + if( viewForCell == nil ) // Already create + { + CGRect cellBounds = [cell frame]; + CGRect progressRect = CGRectMake(cellBounds.size.width * 0.70f, cellBounds.size.height / 2 - 5, cellBounds.size.width * 0.25f, 10); + LDProgressView* rssi_view = [[LDProgressView alloc] initWithFrame:progressRect]; //[[LDProgressView alloc] initWithProgressViewStyle : UIProgressViewStyleDefault]; + [rssi_view setTag:indexPath.row + kRSSIView_Tag]; + rssi_view.showText = @NO; + rssi_view.borderRadius = @5; + rssi_view.type = LDProgressSolid; + [cell addSubview:rssi_view]; + + viewForCell = rssi_view; + } + // Set signal strength as percent. + // Assuming RSSI Value range -100 ~ 0 + float percentage = (p.RSSI + 100) / 100.f; + viewForCell.progress = percentage; + [cell setPeripheral:p]; - return cell; } diff --git a/Mooshimeter/Vendor/KxMenu/KxMenu.h b/Mooshimeter/Vendor/KxMenu/KxMenu.h new file mode 100644 index 0000000..7997b0e --- /dev/null +++ b/Mooshimeter/Vendor/KxMenu/KxMenu.h @@ -0,0 +1,78 @@ +// +// KxMenu.h +// kxmenu project +// https://github.com/kolyvan/kxmenu/ +// +// Created by Kolyvan on 17.05.13. +// + +/* + Copyright (c) 2013 Konstantin Bukreev. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#import + +@interface KxMenuItem : NSObject + +@property (readwrite, nonatomic, strong) UIImage *image; +@property (readwrite, nonatomic, strong) NSString *title; +@property (readwrite, nonatomic, weak) id target; +@property (readwrite, nonatomic) SEL action; +@property (readwrite, nonatomic, strong) UIColor *foreColor; +@property (readwrite, nonatomic) NSTextAlignment alignment; + ++ (instancetype) menuItem:(NSString *) title + image:(UIImage *) image + target:(id)target + action:(SEL) action; + +@end + +@interface KxMenu : UIView + +@property (readwrite, nonatomic, strong) UIFont *titleFont; +@property (readwrite, nonatomic, strong) UIColor *tintColor; +@property (readwrite, nonatomic, strong) UIColor *tintColor1; +@property (readwrite, nonatomic, strong) UIColor *selectedColor; +@property (readwrite, nonatomic, strong) UIColor *selectedColor1; +@property (readwrite, nonatomic, strong) NSArray *menuItems; +@property (readwrite, nonatomic) BOOL blurredBackground; +@property (readwrite, nonatomic, strong) Class overlayClass; + ++ (instancetype) showMenuInView:(UIView *)view + fromRect:(CGRect)rect + menuItems:(NSArray *)menuItems; + +- (void) showMenuInView:(UIView *)view + fromRect:(CGRect)rect; + +- (void)dismissMenu; +- (void)dismissMenu:(BOOL) animated; + +@end + +@interface KxMenuOverlay : UIView +- (KxMenu *) menuView; +@end diff --git a/Mooshimeter/Vendor/KxMenu/KxMenu.m b/Mooshimeter/Vendor/KxMenu/KxMenu.m new file mode 100644 index 0000000..c2b820e --- /dev/null +++ b/Mooshimeter/Vendor/KxMenu/KxMenu.m @@ -0,0 +1,872 @@ +// +// KxMenu.m +// kxmenu project +// https://github.com/kolyvan/kxmenu/ +// +// Created by Kolyvan on 17.05.13. +// + +/* + Copyright (c) 2013 Konstantin Bukreev. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + Some ideas was taken from QBPopupMenu project by Katsuma Tanaka. + https://github.com/questbeat/QBPopupMenu +*/ + +#import "KxMenu.h" +#import +#import "UIImage+ImageEffects.h" +@import Accelerate; + +const CGFloat kArrowSize = 12.f; + +//////////////////////////////////////////////////////////////////////////////// + +@implementation KxMenuOverlay + +//- (void) dealloc { NSLog(@"dealloc <%@ %p>", [self class], self); } + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + } + return self; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + UIView *touched = [[touches anyObject] view]; + if (touched == self) { + //[self.nextResponder touchesBegan:touches withEvent:event]; + [self.menuView dismissMenu:YES]; + } +} + +- (KxMenu *) menuView +{ + for (UIView *v in self.subviews) { + if ([v isKindOfClass:[KxMenu class]]) { + return (KxMenu *)v; + } + } + return nil; +} + +@end + +//////////////////////////////////////////////////////////////////////////////// + +@implementation KxMenuItem + ++ (instancetype) menuItem:(NSString *) title + image:(UIImage *) image + target:(id)target + action:(SEL) action +{ + return [[KxMenuItem alloc] init:title + image:image + target:target + action:action]; +} + +- (id) init:(NSString *) title + image:(UIImage *) image + target:(id)target + action:(SEL) action +{ + NSParameterAssert(title.length || image); + + self = [super init]; + if (self) { + + _title = title; + _image = image; + _target = target; + _action = action; + } + return self; +} + +- (BOOL) enabled +{ + return _target != nil && _action != NULL; +} + +- (void) performAction +{ + __strong id target = self.target; + + if (target && [target respondsToSelector:_action]) { + + [target performSelectorOnMainThread:_action withObject:self waitUntilDone:YES]; + } +} + +- (NSString *) description +{ + return [NSString stringWithFormat:@"<%@ #%p %@>", [self class], self, _title]; +} + +@end + +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + + KxMenuViewArrowDirectionNone, + KxMenuViewArrowDirectionUp, + KxMenuViewArrowDirectionDown, + KxMenuViewArrowDirectionLeft, + KxMenuViewArrowDirectionRight, + +} KxMenuViewArrowDirection; + +@implementation KxMenu { + + KxMenuViewArrowDirection _arrowDirection; + CGFloat _arrowPosition; + UIView *_contentView; + NSArray *_menuItems; + UIImage *_backImage; + BOOL _didObserve; +} + +- (id)init +{ + self = [super initWithFrame:CGRectZero]; + if(self) { + + self.backgroundColor = [UIColor clearColor]; + self.opaque = YES; + self.alpha = 0; + + _didObserve = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(orientationWillChange:) + name:UIApplicationWillChangeStatusBarOrientationNotification + object:nil]; + } + + return self; +} + +- (void) dealloc +{ + if (_didObserve) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } + //NSLog(@"dealloc <%@ %p>", [self class], self); +} + +- (void) orientationWillChange: (NSNotification *) n +{ + [self dismissMenu:NO]; +} + +- (void) setupFrameInView:(UIView *)view + fromRect:(CGRect)fromRect +{ + const CGSize contentSize = _contentView.frame.size; + + const CGFloat outerWidth = view.bounds.size.width; + const CGFloat outerHeight = view.bounds.size.height; + + const CGFloat rectX0 = fromRect.origin.x; + const CGFloat rectX1 = fromRect.origin.x + fromRect.size.width; + const CGFloat rectXM = fromRect.origin.x + fromRect.size.width * 0.5f; + const CGFloat rectY0 = fromRect.origin.y; + const CGFloat rectY1 = fromRect.origin.y + fromRect.size.height; + const CGFloat rectYM = fromRect.origin.y + fromRect.size.height * 0.5f;; + + const CGFloat widthPlusArrow = contentSize.width + kArrowSize; + const CGFloat heightPlusArrow = contentSize.height + kArrowSize; + const CGFloat widthHalf = contentSize.width * 0.5f; + const CGFloat heightHalf = contentSize.height * 0.5f; + + const CGFloat kMargin = 5.f; + + if (heightPlusArrow < (outerHeight - rectY1)) { + + _arrowDirection = KxMenuViewArrowDirectionUp; + CGPoint point = (CGPoint){ + rectXM - widthHalf, + rectY1 + }; + + if (point.x < kMargin) + point.x = kMargin; + + if ((point.x + contentSize.width + kMargin) > outerWidth) + point.x = outerWidth - contentSize.width - kMargin; + + _arrowPosition = rectXM - point.x; + _arrowPosition = MAX(20.f, MIN(_arrowPosition, contentSize.width - 20.f)); + _contentView.frame = (CGRect){0, kArrowSize, contentSize}; + + self.frame = (CGRect) { + + point, + contentSize.width, + contentSize.height + kArrowSize + }; + + } else if (heightPlusArrow < rectY0) { + + _arrowDirection = KxMenuViewArrowDirectionDown; + CGPoint point = (CGPoint){ + rectXM - widthHalf, + rectY0 - heightPlusArrow + }; + + if (point.x < kMargin) + point.x = kMargin; + + if ((point.x + contentSize.width + kMargin) > outerWidth) + point.x = outerWidth - contentSize.width - kMargin; + + _arrowPosition = rectXM - point.x; + _arrowPosition = MAX(20.f, MIN(_arrowPosition, contentSize.width - 20.f)); + _contentView.frame = (CGRect){CGPointZero, contentSize}; + + self.frame = (CGRect) { + + point, + contentSize.width, + contentSize.height + kArrowSize + }; + + } else if (widthPlusArrow < (outerWidth - rectX1)) { + + _arrowDirection = KxMenuViewArrowDirectionLeft; + CGPoint point = (CGPoint){ + rectX1, + rectYM - heightHalf + }; + + if (point.y < kMargin) + point.y = kMargin; + + if ((point.y + contentSize.height + kMargin) > outerHeight) + point.y = outerHeight - contentSize.height - kMargin; + + _arrowPosition = rectYM - point.y; + _contentView.frame = (CGRect){kArrowSize, 0, contentSize}; + + self.frame = (CGRect) { + + point, + contentSize.width + kArrowSize, + contentSize.height + }; + + } else if (widthPlusArrow < rectX0) { + + _arrowDirection = KxMenuViewArrowDirectionRight; + CGPoint point = (CGPoint){ + rectX0 - widthPlusArrow, + rectYM - heightHalf + }; + + if (point.y < kMargin) + point.y = kMargin; + + if ((point.y + contentSize.height + 5) > outerHeight) + point.y = outerHeight - contentSize.height - kMargin; + + _arrowPosition = rectYM - point.y; + _contentView.frame = (CGRect){CGPointZero, contentSize}; + + self.frame = (CGRect) { + + point, + contentSize.width + kArrowSize, + contentSize.height + }; + + } else { + + _arrowDirection = KxMenuViewArrowDirectionNone; + + self.frame = (CGRect) { + + (outerWidth - contentSize.width) * 0.5f, + (outerHeight - contentSize.height) * 0.5f, + contentSize, + }; + } +} + +- (void)showMenuInView:(UIView *)view + fromRect:(CGRect)rect +{ + _contentView = [self mkContentView]; + [self addSubview:_contentView]; + + [self setupFrameInView:view fromRect:rect]; + + _contentView.hidden = YES; + + const CGRect toFrame = self.frame; + + if (_blurredBackground) { + + UIColor *tintColor = self.tintColor; + if (!tintColor) { + tintColor = [UIColor colorWithRed:0.04f green:0.04f blue:0.04f alpha:0.5f]; + } + + _backImage = [KxMenu blurredBackground:view + inRect:toFrame + tintColor:tintColor]; + } + + self.frame = (CGRect){self.arrowPoint, 1, 1}; + + Class overlayClass = _overlayClass ? _overlayClass : [KxMenuOverlay class]; + KxMenuOverlay *overlay = [[overlayClass alloc] initWithFrame:view.bounds]; + [overlay addSubview:self]; + [view addSubview:overlay]; + + [self setNeedsDisplay]; + + [UIView animateWithDuration:0.2 + animations:^(void) { + + self.alpha = 1.0f; + self.frame = toFrame; + + } completion:^(BOOL completed) { + + _contentView.hidden = NO; + }]; + +} + +- (void)dismissMenu +{ + [self dismissMenu:YES]; +} + +- (void)dismissMenu:(BOOL) animated +{ + if (!self.superview) { + return; + } + + if (animated) { + + if (_contentView.hidden) { + [self dismissMenu:NO]; + return; + } + + _contentView.hidden = YES; + const CGRect toFrame = (CGRect){self.arrowPoint, 1, 1}; + + [UIView animateWithDuration:0.2 + animations:^(void) { + + self.alpha = 0; + self.frame = toFrame; + + } completion:^(BOOL finished) { + + [self dismissMenu:NO]; + }]; + + } else { + + Class overlayClass = _overlayClass ? _overlayClass : [KxMenuOverlay class]; + UIView *v = self.superview; + [self removeFromSuperview]; + if ([v isKindOfClass:overlayClass]) { + [v removeFromSuperview]; + } + } +} + +- (void)performAction:(id)sender +{ + [self dismissMenu:YES]; + + UIButton *button = (UIButton *)sender; + KxMenuItem *menuItem = _menuItems[button.tag]; + [menuItem performAction]; +} + +- (UIView *) mkContentView +{ + for (UIView *v in self.subviews) { + [v removeFromSuperview]; + } + + if (!_menuItems.count) + return nil; + + const CGFloat kMinMenuItemHeight = 32.f; + const CGFloat kMinMenuItemWidth = 32.f; + const CGFloat kMarginX = 10.f; + const CGFloat kMarginY = 5.f; + + UIFont *titleFont = self.titleFont; + if (!titleFont) titleFont = [UIFont boldSystemFontOfSize:16]; + + CGFloat maxImageWidth = 0; + CGFloat maxItemHeight = 0; + CGFloat maxItemWidth = 0; + + for (KxMenuItem *menuItem in _menuItems) { + + const CGSize imageSize = menuItem.image.size; + if (imageSize.width > maxImageWidth) + maxImageWidth = imageSize.width; + } + + if (maxImageWidth) { + maxImageWidth += kMarginX; + } + + for (KxMenuItem *menuItem in _menuItems) { + + const CGSize titleSize = [menuItem.title sizeWithFont:titleFont]; + const CGSize imageSize = menuItem.image.size; + + const CGFloat itemHeight = MAX(titleSize.height, imageSize.height) + kMarginY * 2; + const CGFloat itemWidth = ((!menuItem.enabled && !menuItem.image) ? titleSize.width : maxImageWidth + titleSize.width) + kMarginX * 4; + + if (itemHeight > maxItemHeight) + maxItemHeight = itemHeight; + + if (itemWidth > maxItemWidth) + maxItemWidth = itemWidth; + } + + maxItemWidth = MAX(maxItemWidth, kMinMenuItemWidth); + maxItemHeight = MAX(maxItemHeight, kMinMenuItemHeight); + + const CGFloat titleX = kMarginX * 2 + maxImageWidth; + const CGFloat titleWidth = maxItemWidth - titleX - kMarginX * 2; + + UIImage *selectedImage = [self selectedImage:(CGSize){maxItemWidth, maxItemHeight + 2}]; + UIImage *gradientLine = [KxMenu gradientLine: (CGSize){maxItemWidth - kMarginX * 4, 1}]; + + UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero]; + contentView.autoresizingMask = UIViewAutoresizingNone; + contentView.backgroundColor = [UIColor clearColor]; + contentView.opaque = NO; + + CGFloat itemY = kMarginY * 2; + NSUInteger itemNum = 0; + + for (KxMenuItem *menuItem in _menuItems) { + + const CGRect itemFrame = (CGRect){0, itemY, maxItemWidth, maxItemHeight}; + + UIView *itemView = [[UIView alloc] initWithFrame:itemFrame]; + itemView.autoresizingMask = UIViewAutoresizingNone; + itemView.backgroundColor = [UIColor clearColor]; + itemView.opaque = NO; + + [contentView addSubview:itemView]; + + if (menuItem.enabled) { + + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.tag = itemNum; + button.frame = itemView.bounds; + button.enabled = menuItem.enabled; + button.backgroundColor = [UIColor clearColor]; + button.opaque = NO; + button.autoresizingMask = UIViewAutoresizingNone; + + [button addTarget:self + action:@selector(performAction:) + forControlEvents:UIControlEventTouchUpInside]; + + [button setBackgroundImage:selectedImage forState:UIControlStateHighlighted]; + + [itemView addSubview:button]; + } + + if (menuItem.title.length) { + + CGRect titleFrame; + + if (!menuItem.enabled && !menuItem.image) { + + titleFrame = (CGRect){ + kMarginX * 2, + kMarginY, + maxItemWidth - kMarginX * 4, + maxItemHeight - kMarginY * 2 + }; + + } else { + + titleFrame = (CGRect){ + titleX, + kMarginY, + titleWidth, + maxItemHeight - kMarginY * 2 + }; + } + + UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleFrame]; + titleLabel.text = menuItem.title; + titleLabel.font = titleFont; + titleLabel.textAlignment = menuItem.alignment; + titleLabel.textColor = menuItem.foreColor ? menuItem.foreColor : [UIColor whiteColor]; + titleLabel.backgroundColor = [UIColor clearColor]; + titleLabel.autoresizingMask = UIViewAutoresizingNone; + //titleLabel.backgroundColor = [UIColor greenColor]; + [itemView addSubview:titleLabel]; + } + + if (menuItem.image) { + + const CGRect imageFrame = {kMarginX * 2, kMarginY, maxImageWidth, maxItemHeight - kMarginY * 2}; + UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageFrame]; + imageView.image = menuItem.image; + imageView.clipsToBounds = YES; + imageView.contentMode = UIViewContentModeCenter; + imageView.autoresizingMask = UIViewAutoresizingNone; + [itemView addSubview:imageView]; + } + + if (itemNum < _menuItems.count - 1) { + + UIImageView *gradientView = [[UIImageView alloc] initWithImage:gradientLine]; + gradientView.frame = (CGRect){kMarginX * 2, maxItemHeight + 1, gradientLine.size}; + gradientView.contentMode = UIViewContentModeLeft; + [itemView addSubview:gradientView]; + + itemY += 2; + } + + itemY += maxItemHeight; + ++itemNum; + } + + contentView.frame = (CGRect){0, 0, maxItemWidth, itemY + kMarginY * 2}; + + return contentView; +} + +- (CGPoint) arrowPoint +{ + CGPoint point; + + if (_arrowDirection == KxMenuViewArrowDirectionUp) { + + point = (CGPoint){ CGRectGetMinX(self.frame) + _arrowPosition, CGRectGetMinY(self.frame) }; + + } else if (_arrowDirection == KxMenuViewArrowDirectionDown) { + + point = (CGPoint){ CGRectGetMinX(self.frame) + _arrowPosition, CGRectGetMaxY(self.frame) }; + + } else if (_arrowDirection == KxMenuViewArrowDirectionLeft) { + + point = (CGPoint){ CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + _arrowPosition }; + + } else if (_arrowDirection == KxMenuViewArrowDirectionRight) { + + point = (CGPoint){ CGRectGetMaxX(self.frame), CGRectGetMinY(self.frame) + _arrowPosition }; + + } else { + + point = self.center; + } + + return point; +} + +- (UIImage *) selectedImage:(CGSize) size +{ + CGFloat R0 = 0.216, G0 = 0.471, B0 = 0.871; + CGFloat R1 = 0.059, G1 = 0.353, B1 = 0.839; + + if (_selectedColor) { + CGFloat a; + [_selectedColor getRed:&R0 green:&G0 blue:&B0 alpha:&a]; + } + + if (_selectedColor1) { + CGFloat a; + [_selectedColor1 getRed:&R1 green:&G1 blue:&B1 alpha:&a]; + } + + const CGFloat locations[] = {0,1}; + const CGFloat components[] = { + R0, G0, B0, 1, + R1, G1, B1, 1, + }; + + return [KxMenu gradientImageWithSize:size locations:locations components:components count:2]; +} + ++ (UIImage *) gradientLine:(CGSize) size +{ + const CGFloat locations[5] = {0,0.2,0.5,0.8,1}; + + const CGFloat R = 0.44f, G = 0.44f, B = 0.44f; + + const CGFloat components[20] = { + R,G,B,0.1, + R,G,B,0.4, + R,G,B,0.7, + R,G,B,0.4, + R,G,B,0.1 + }; + + return [self gradientImageWithSize:size locations:locations components:components count:5]; +} + ++ (UIImage *) gradientImageWithSize:(CGSize) size + locations:(const CGFloat []) locations + components:(const CGFloat []) components + count:(NSUInteger)count +{ + UIGraphicsBeginImageContextWithOptions(size, NO, 0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef colorGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2); + CGColorSpaceRelease(colorSpace); + CGContextDrawLinearGradient(context, colorGradient, (CGPoint){0, 0}, (CGPoint){size.width, 0}, 0); + CGGradientRelease(colorGradient); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + +- (void) drawRect:(CGRect)rect +{ + [self drawBackground:self.bounds + inContext:UIGraphicsGetCurrentContext()]; +} + +- (void)drawBackground:(CGRect)frame + inContext:(CGContextRef) context +{ + CGFloat X0 = frame.origin.x; + CGFloat X1 = frame.origin.x + frame.size.width; + CGFloat Y0 = frame.origin.y; + CGFloat Y1 = frame.origin.y + frame.size.height; + + // render arrow + + UIBezierPath *arrowPath = [UIBezierPath bezierPath]; + + if (_arrowDirection == KxMenuViewArrowDirectionUp) { + + const CGFloat arrowXM = _arrowPosition; + const CGFloat arrowX0 = arrowXM - kArrowSize; + const CGFloat arrowX1 = arrowXM + kArrowSize; + const CGFloat arrowY0 = Y0; + const CGFloat arrowY1 = Y0 + kArrowSize; + + [arrowPath moveToPoint: (CGPoint){arrowXM, arrowY0}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}]; + [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowY1}]; + [arrowPath addLineToPoint: (CGPoint){arrowXM, arrowY0}]; + + Y0 += kArrowSize; + + } else if (_arrowDirection == KxMenuViewArrowDirectionDown) { + + const CGFloat arrowXM = _arrowPosition; + const CGFloat arrowX0 = arrowXM - kArrowSize; + const CGFloat arrowX1 = arrowXM + kArrowSize; + const CGFloat arrowY0 = Y1 - kArrowSize; + const CGFloat arrowY1 = Y1; + + [arrowPath moveToPoint: (CGPoint){arrowXM, arrowY1}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}]; + [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowY0}]; + [arrowPath addLineToPoint: (CGPoint){arrowXM, arrowY1}]; + + Y1 -= kArrowSize; + + } else if (_arrowDirection == KxMenuViewArrowDirectionLeft) { + + const CGFloat arrowYM = _arrowPosition; + const CGFloat arrowX0 = X0; + const CGFloat arrowX1 = X0 + kArrowSize; + const CGFloat arrowY0 = arrowYM - kArrowSize;; + const CGFloat arrowY1 = arrowYM + kArrowSize; + + [arrowPath moveToPoint: (CGPoint){arrowX0, arrowYM}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}]; + [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowYM}]; + + X0 += kArrowSize; + + } else if (_arrowDirection == KxMenuViewArrowDirectionRight) { + + const CGFloat arrowYM = _arrowPosition; + const CGFloat arrowX0 = X1; + const CGFloat arrowX1 = X1 - kArrowSize; + const CGFloat arrowY0 = arrowYM - kArrowSize;; + const CGFloat arrowY1 = arrowYM + kArrowSize; + + [arrowPath moveToPoint: (CGPoint){arrowX0, arrowYM}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}]; + [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}]; + [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowYM}]; + + X1 -= kArrowSize; + } + + const CGRect bodyFrame = {X0, Y0, X1 - X0, Y1 - Y0}; + + UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:bodyFrame + cornerRadius:8]; + + if (_backImage) + { + [borderPath appendPath:arrowPath]; + [borderPath addClip]; + [_backImage drawInRect:frame]; + + } else { + + CGFloat R0 = 0.267, G0 = 0.303, B0 = 0.335; + CGFloat R1 = 0.040, G1 = 0.040, B1 = 0.040; + + if (_tintColor) { + CGFloat a; + [_tintColor getRed:&R0 green:&G0 blue:&B0 alpha:&a]; + } + + if (_tintColor1) { + CGFloat a; + [_tintColor1 getRed:&R1 green:&G1 blue:&B1 alpha:&a]; + } + + if (_arrowDirection == KxMenuViewArrowDirectionUp || + _arrowDirection == KxMenuViewArrowDirectionLeft) { + + [[UIColor colorWithRed:R0 green:G0 blue:B0 alpha:1] set]; + + } else { + + [[UIColor colorWithRed:R1 green:G1 blue:B1 alpha:1] set]; + } + + [arrowPath fill]; + + // render body + + const CGFloat locations[] = {0, 1}; + const CGFloat components[] = { + R0, G0, B0, 1, + R1, G1, B1, 1, + }; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, + components, + locations, + sizeof(locations)/sizeof(locations[0])); + CGColorSpaceRelease(colorSpace); + + + [borderPath addClip]; + + CGPoint start, end; + + if (_arrowDirection == KxMenuViewArrowDirectionLeft || + _arrowDirection == KxMenuViewArrowDirectionRight) { + + start = (CGPoint){X0, Y0}; + end = (CGPoint){X1, Y0}; + + } else { + + start = (CGPoint){X0, Y0}; + end = (CGPoint){X0, Y1}; + } + + CGContextDrawLinearGradient(context, gradient, start, end, 0); + + CGGradientRelease(gradient); + } +} + ++ (UIImage *) blurredBackground:(UIView *)v + inRect:(CGRect)rect + tintColor:(UIColor *)tintColor +{ + const CGFloat screenScale = 1.0f; //v.window.screen.scale; + + UIImage *image; + + UIGraphicsBeginImageContextWithOptions(rect.size, NO, screenScale); + + CGContextTranslateCTM(UIGraphicsGetCurrentContext(), + -rect.origin.x, + -rect.origin.y); + + if ([v respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)] && + [v drawViewHierarchyInRect:v.bounds afterScreenUpdates:NO]) + { + image = UIGraphicsGetImageFromCurrentImageContext(); + } + + if (!image) { + + [v.layer renderInContext:UIGraphicsGetCurrentContext()]; + image = UIGraphicsGetImageFromCurrentImageContext(); + } + + UIGraphicsEndImageContext(); + + return [image applyBlurWithRadius:6 + tintColor:tintColor + saturationDeltaFactor:1.8 + maskImage:nil]; +} + ++ (instancetype) showMenuInView:(UIView *)view + fromRect:(CGRect)rect + menuItems:(NSArray *)menuItems +{ + KxMenu *menu = [[self alloc] init]; + menu.menuItems = menuItems; + dispatch_async(dispatch_get_main_queue(), ^{ + // it allows to tune parameters before showing menu + [menu showMenuInView:view fromRect:rect]; + }); + return menu; +} + +@end diff --git a/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.h b/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.h new file mode 100644 index 0000000..3b41fef --- /dev/null +++ b/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.h @@ -0,0 +1,17 @@ + + +#import + +@interface UIImage (ImageEffects) + +- (UIImage *)applyLightEffect; +- (UIImage *)applyExtraLightEffect; +- (UIImage *)applyDarkEffect; +- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor; + +- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius + tintColor:(UIColor *)tintColor + saturationDeltaFactor:(CGFloat)saturationDeltaFactor + maskImage:(UIImage *)maskImage; + +@end diff --git a/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.m b/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.m new file mode 100644 index 0000000..87c9ffe --- /dev/null +++ b/Mooshimeter/Vendor/KxMenu/UIImage+ImageEffects.m @@ -0,0 +1,185 @@ + + +#import "UIImage+ImageEffects.h" + +#import +#import + + +@implementation UIImage (ImageEffects) + + +- (UIImage *)applyLightEffect +{ + UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3]; + return [self applyBlurWithRadius:30 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; +} + + +- (UIImage *)applyExtraLightEffect +{ + UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82]; + return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; +} + + +- (UIImage *)applyDarkEffect +{ + UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73]; + return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; +} + + +- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor +{ + const CGFloat EffectColorAlpha = 0.6; + UIColor *effectColor = tintColor; + int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor); + if (componentCount == 2) { + CGFloat b; + if ([tintColor getWhite:&b alpha:NULL]) { + effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha]; + } + } + else { + CGFloat r, g, b; + if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) { + effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha]; + } + } + return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil]; +} + + +- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage +{ + // Check pre-conditions. + if (self.size.width < 1 || self.size.height < 1) { + NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self); + return nil; + } + if (!self.CGImage) { + NSLog (@"*** error: image must be backed by a CGImage: %@", self); + return nil; + } + if (maskImage && !maskImage.CGImage) { + NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage); + return nil; + } + + CGRect imageRect = { CGPointZero, self.size }; + UIImage *effectImage = self; + + BOOL hasBlur = blurRadius > __FLT_EPSILON__; + BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__; + if (hasBlur || hasSaturationChange) { + UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); + CGContextRef effectInContext = UIGraphicsGetCurrentContext(); + CGContextScaleCTM(effectInContext, 1.0, -1.0); + CGContextTranslateCTM(effectInContext, 0, -self.size.height); + CGContextDrawImage(effectInContext, imageRect, self.CGImage); + + vImage_Buffer effectInBuffer; + effectInBuffer.data = CGBitmapContextGetData(effectInContext); + effectInBuffer.width = CGBitmapContextGetWidth(effectInContext); + effectInBuffer.height = CGBitmapContextGetHeight(effectInContext); + effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext); + + UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); + CGContextRef effectOutContext = UIGraphicsGetCurrentContext(); + vImage_Buffer effectOutBuffer; + effectOutBuffer.data = CGBitmapContextGetData(effectOutContext); + effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext); + effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext); + effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext); + + if (hasBlur) { + // A description of how to compute the box kernel width from the Gaussian + // radius (aka standard deviation) appears in the SVG spec: + // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement + // + // For larger values of 's' (s >= 2.0), an approximation can be used: Three + // successive box-blurs build a piece-wise quadratic convolution kernel, which + // approximates the Gaussian kernel to within roughly 3%. + // + // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) + // + // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. + // + CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale]; + NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); + if (radius % 2 != 1) { + radius += 1; // force radius to be odd so that the three box-blur methodology works. + } + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + } + BOOL effectImageBuffersAreSwapped = NO; + if (hasSaturationChange) { + CGFloat s = saturationDeltaFactor; + CGFloat floatingPointSaturationMatrix[] = { + 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0, + 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0, + 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0, + 0, 0, 0, 1, + }; + const int32_t divisor = 256; + NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]); + int16_t saturationMatrix[matrixSize]; + for (NSUInteger i = 0; i < matrixSize; ++i) { + saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor); + } + if (hasBlur) { + vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); + effectImageBuffersAreSwapped = YES; + } + else { + vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); + } + } + if (!effectImageBuffersAreSwapped) + effectImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + if (effectImageBuffersAreSwapped) + effectImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + + // Set up output context. + UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); + CGContextRef outputContext = UIGraphicsGetCurrentContext(); + CGContextScaleCTM(outputContext, 1.0, -1.0); + CGContextTranslateCTM(outputContext, 0, -self.size.height); + + // Draw base image. + CGContextDrawImage(outputContext, imageRect, self.CGImage); + + // Draw effect image. + if (hasBlur) { + CGContextSaveGState(outputContext); + if (maskImage) { + CGContextClipToMask(outputContext, imageRect, maskImage.CGImage); + } + CGContextDrawImage(outputContext, imageRect, effectImage.CGImage); + CGContextRestoreGState(outputContext); + } + + // Add in color tint. + if (tintColor) { + CGContextSaveGState(outputContext); + CGContextSetFillColorWithColor(outputContext, tintColor.CGColor); + CGContextFillRect(outputContext, imageRect); + CGContextRestoreGState(outputContext); + } + + // Output image is ready. + UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return outputImage; +} + + +@end diff --git a/Mooshimeter/Vendor/LDProgressView/LDProgressView.h b/Mooshimeter/Vendor/LDProgressView/LDProgressView.h new file mode 100755 index 0000000..7eec2bc --- /dev/null +++ b/Mooshimeter/Vendor/LDProgressView/LDProgressView.h @@ -0,0 +1,29 @@ +// +// LDProgressView.h +// LDProgressView +// +// Created by Christian Di Lorenzo on 9/27/13. +// Copyright (c) 2013 Light Design. All rights reserved. +// + +#import + +typedef enum { + LDProgressStripes, + LDProgressGradient, + LDProgressSolid +} LDProgressType; + +@interface LDProgressView : UIView + +@property (nonatomic) CGFloat progress; + +@property (nonatomic, strong) UIColor *color UI_APPEARANCE_SELECTOR; +@property (nonatomic, strong) NSNumber *flat UI_APPEARANCE_SELECTOR; +@property (nonatomic, strong) NSNumber *animate UI_APPEARANCE_SELECTOR; +@property (nonatomic, strong) NSNumber *showText UI_APPEARANCE_SELECTOR; +@property (nonatomic, strong) NSNumber *borderRadius UI_APPEARANCE_SELECTOR; + +@property (nonatomic) LDProgressType type; + +@end diff --git a/Mooshimeter/Vendor/LDProgressView/LDProgressView.m b/Mooshimeter/Vendor/LDProgressView/LDProgressView.m new file mode 100755 index 0000000..01741e2 --- /dev/null +++ b/Mooshimeter/Vendor/LDProgressView/LDProgressView.m @@ -0,0 +1,276 @@ +// +// LDProgressView.m +// LDProgressView +// +// Created by Christian Di Lorenzo on 9/27/13. +// Copyright (c) 2013 Light Design. All rights reserved. +// + +#import "LDProgressView.h" +#import "UIColor+RGBValues.h" + +@interface LDProgressView () +@property (nonatomic) CGFloat offset; +@property (nonatomic, strong) NSTimer *timer; +@property (nonatomic) CGFloat stripeWidth; +@property (nonatomic, strong) UIImage *gradientProgress; +@property (nonatomic) CGSize stripeSize; + +// Animation of progress +@property (nonatomic, strong) NSTimer *animationTimer; +@property (nonatomic) CGFloat progressToAnimateTo; +@end + +@implementation LDProgressView +@synthesize animate=_animate, color=_color; + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self initialize]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self initialize]; + } + return self; +} + +- (void)initialize { + self.backgroundColor = [UIColor clearColor]; +} + +- (void)setAnimate:(NSNumber *)animate { + _animate = animate; + if ([animate boolValue]) { + self.timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(incrementOffset) userInfo:nil repeats:YES]; + } else if (self.timer) { + [self.timer invalidate]; + } +} + +- (void)setProgress:(CGFloat)progress { + self.progressToAnimateTo = progress; + if (self.animationTimer) { + [self.animationTimer invalidate]; + } + self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.008 target:self selector:@selector(incrementAnimatingProgress) userInfo:nil repeats:YES]; +} + +- (void)incrementAnimatingProgress { + if (_progress >= self.progressToAnimateTo-0.01 && _progress <= self.progressToAnimateTo+0.01) { + _progress = self.progressToAnimateTo; + [self.animationTimer invalidate]; + [self setNeedsDisplay]; + } else { + _progress = (_progress < self.progressToAnimateTo) ? _progress + 0.01 : _progress - 0.01; + [self setNeedsDisplay]; + } +} + +- (void)incrementOffset { + if (self.offset >= 0) { + self.offset = -self.stripeWidth; + } else { + self.offset += 1; + } + [self setNeedsDisplay]; +} + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + [self drawProgressBackground:context inRect:rect]; + if (self.progress > 0) { + [self drawProgress:context withFrame:rect]; + } +} + +- (void)drawProgressBackground:(CGContextRef)context inRect:(CGRect)rect { + CGContextSaveGState(context); + UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.borderRadius.floatValue]; + CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.51f green:0.51f blue:0.51f alpha:1.00f].CGColor); + [roundedRect fill]; + + UIBezierPath *roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect:CGRectMake(-10, -10, rect.size.width+10, rect.size.height+10)]; + [roundedRectangleNegativePath appendPath:roundedRect]; + roundedRectangleNegativePath.usesEvenOddFillRule = YES; + + CGSize shadowOffset = CGSizeMake(0.5, 1); + CGContextSaveGState(context); + CGFloat xOffset = shadowOffset.width + round(rect.size.width); + CGFloat yOffset = shadowOffset.height; + CGContextSetShadowWithColor(context, + CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)), 5, [[UIColor blackColor] colorWithAlphaComponent:0.7].CGColor); + + [roundedRect addClip]; + CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(rect.size.width), 0); + [roundedRectangleNegativePath applyTransform:transform]; + [[UIColor grayColor] setFill]; + [roundedRectangleNegativePath fill]; + CGContextRestoreGState(context); + + // Add clip for drawing progress + [roundedRect addClip]; +} + +- (void)drawProgress:(CGContextRef)context withFrame:(CGRect)frame { + CGRect rectToDrawIn = CGRectMake(0, 0, frame.size.width * self.progress, frame.size.height); + CGRect insetRect = CGRectInset(rectToDrawIn, self.progress > 0.03 ? 0.5 : -0.5, 0.5); + + UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.borderRadius.floatValue]; + if ([self.flat boolValue]) { + CGContextSetFillColorWithColor(context, self.color.CGColor); + [roundedRect fill]; + } else { + CGContextSaveGState(context); + [roundedRect addClip]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat locations[] = {0.0, 1.0}; + NSArray *colors = @[(__bridge id)[self.color lighterColor].CGColor, (__bridge id)[self.color darkerColor].CGColor]; + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations); + + CGContextDrawLinearGradient(context, gradient, CGPointMake(insetRect.size.width / 2, 0), CGPointMake(insetRect.size.width / 2, insetRect.size.height), 0); + CGContextRestoreGState(context); + + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + } + + if (self.progress != 1.0) { + switch (self.type) { + case LDProgressGradient: + [self drawGradients:context inRect:insetRect]; + break; + case LDProgressStripes: + [self drawStripes:context inRect:insetRect]; + break; + default: + break; + } + } + CGContextSetStrokeColorWithColor(context, [[self.color darkerColor] darkerColor].CGColor); + [roundedRect stroke]; + + if ([self.showText boolValue]) { + [self drawRightAlignedLabelInRect:insetRect]; + } +} + +- (void)drawGradients:(CGContextRef)context inRect:(CGRect)rect { + self.stripeSize = CGSizeMake(self.stripeWidth, rect.size.height); + CGContextSaveGState(context); + [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.borderRadius.floatValue] addClip]; + CGFloat xStart = self.offset; + while (xStart < rect.size.width) { + [self.gradientProgress drawAtPoint:CGPointMake(xStart, 0)]; + xStart += self.stripeWidth; + } + CGContextRestoreGState(context); +} + +- (void)drawStripes:(CGContextRef)context inRect:(CGRect)rect { + CGContextSaveGState(context); + [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.borderRadius.floatValue] addClip]; + CGContextSetFillColorWithColor(context, [[UIColor whiteColor] colorWithAlphaComponent:0.2].CGColor); + CGFloat xStart = self.offset, height = rect.size.height, width = self.stripeWidth; + while (xStart < rect.size.width) { + CGContextSaveGState(context); + CGContextMoveToPoint(context, xStart, height); + CGContextAddLineToPoint(context, xStart + width * 0.25, 0); + CGContextAddLineToPoint(context, xStart + width * 0.75, 0); + CGContextAddLineToPoint(context, xStart + width * 0.50, height); + CGContextClosePath(context); + CGContextFillPath(context); + CGContextRestoreGState(context); + xStart += width; + } + CGContextRestoreGState(context); +} + +- (void)drawRightAlignedLabelInRect:(CGRect)rect { + if (rect.size.width > 40) { + UILabel *label = [[UILabel alloc] initWithFrame:rect]; + label.adjustsFontSizeToFitWidth = YES; + label.backgroundColor = [UIColor clearColor]; + label.textAlignment = NSTextAlignmentRight; + label.text = [NSString stringWithFormat:@"%.0f%%", self.progress*100]; + label.font = [UIFont boldSystemFontOfSize:17]; + UIColor *baseLabelColor = [self.color isLighterColor] ? [UIColor blackColor] : [UIColor whiteColor]; + label.textColor = [baseLabelColor colorWithAlphaComponent:0.6]; + [label drawTextInRect:CGRectMake(6, 0, rect.size.width-12, rect.size.height)]; + } +} + +#pragma mark - Accessors + +- (UIImage *)gradientProgress { + if (!_gradientProgress) { + UIGraphicsBeginImageContext(self.stripeSize); + CGContextRef imageCxt = UIGraphicsGetCurrentContext(); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat locations[] = {0.0, 0.5, 1.0}; + NSArray *colors = @[(__bridge id)[UIColor clearColor].CGColor, (__bridge id)[[[self.color darkerColor] darkerColor] colorWithAlphaComponent:0.3].CGColor, (__bridge id)[UIColor clearColor].CGColor]; + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations); + + CGContextDrawLinearGradient(imageCxt, gradient, CGPointMake(0, self.stripeSize.height / 2), CGPointMake(self.stripeSize.width, self.stripeSize.height / 2), 0); + + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + + _gradientProgress = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + return _gradientProgress; +} + +- (NSNumber *)animate { + if (_animate == nil) { + return @YES; + } + return _animate; +} + +- (NSNumber *)showText { + if (_showText == nil) { + return @YES; + } + return _showText; +} + +- (void)setColor:(UIColor *)color { + _color = color; + self.gradientProgress = nil; +} + +- (UIColor *)color { + if (!_color) { + return [UIColor colorWithRed:0.07 green:0.56 blue:1.0 alpha:1.0]; + } + return _color; +} + +- (CGFloat)stripeWidth { + switch (self.type) { + case LDProgressGradient: + _stripeWidth = 15; + break; + default: + _stripeWidth = 50; + break; + } + return _stripeWidth; +} + +- (NSNumber *)borderRadius { + if (!_borderRadius) { + return @(self.frame.size.height / 2.0); + } + return _borderRadius; +} + +@end diff --git a/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.h b/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.h new file mode 100755 index 0000000..96d89a8 --- /dev/null +++ b/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.h @@ -0,0 +1,23 @@ +// +// UIColor+RGBValues.h +// LDBarButtonItemExample +// +// Created by Christian Di Lorenzo on 1/24/13. +// Copyright (c) 2013 Light Design. All rights reserved. +// + +#import + +@interface UIColor (RGBValues) + +- (CGFloat)red; +- (CGFloat)green; +- (CGFloat)blue; +- (CGFloat)alpha; + +- (UIColor *)darkerColor; +- (UIColor *)lighterColor; +- (BOOL)isLighterColor; +- (BOOL)isClearColor; + +@end diff --git a/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.m b/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.m new file mode 100755 index 0000000..d9f2f20 --- /dev/null +++ b/Mooshimeter/Vendor/LDProgressView/UIColor+RGBValues.m @@ -0,0 +1,65 @@ +// +// UIColor+RGBValues.m +// LDBarButtonItemExample +// +// Created by Christian Di Lorenzo on 1/24/13. +// Copyright (c) 2013 Light Design. All rights reserved. +// + +#import "UIColor+RGBValues.h" + +@implementation UIColor (RGBValues) + +- (CGFloat)red { + const CGFloat* components = CGColorGetComponents(self.CGColor); + return components[0]; +} + +- (CGFloat)green { + const CGFloat* components = CGColorGetComponents(self.CGColor); + return components[1]; +} + +- (CGFloat)blue { + const CGFloat* components = CGColorGetComponents(self.CGColor); + return components[2]; +} + +- (CGFloat)alpha { + return CGColorGetAlpha(self.CGColor); +} + +- (BOOL)isClearColor { + return [self isEqual:[UIColor clearColor]]; +} + +- (BOOL)isLighterColor { + const CGFloat* components = CGColorGetComponents(self.CGColor); + return (components[0]+components[1]+components[2])/3 >= 0.5; +} + +- (UIColor *)lighterColor { + if ([self isEqual:[UIColor whiteColor]]) return [UIColor colorWithWhite:0.99 alpha:1.0]; + if ([self isEqual:[UIColor blackColor]]) return [UIColor colorWithWhite:0.01 alpha:1.0]; + float hue, saturation, brightness, alpha; + if ([self getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]) + return [UIColor colorWithHue:hue + saturation:saturation + brightness:MIN(brightness * 1.3, 1.0) + alpha:alpha]; + return nil; +} + +- (UIColor *)darkerColor { + if ([self isEqual:[UIColor whiteColor]]) return [UIColor colorWithWhite:0.99 alpha:1.0]; + if ([self isEqual:[UIColor blackColor]]) return [UIColor colorWithWhite:0.01 alpha:1.0]; + float hue, saturation, brightness, alpha; + if ([self getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]) + return [UIColor colorWithHue:hue + saturation:saturation + brightness:brightness * 0.75 + alpha:alpha]; + return nil; +} + +@end diff --git a/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error.png b/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc7da8a3877ae7e298260d5cf58e861be58f599 GIT binary patch literal 371 zcmV-(0gV2MP)e>AkF=ZKBc${W0UIkXAc83;voHy=WFMmL{_r)~$$W{A*$n|8utp!roFSOZlI5m9YN`bK4s( zNp)+g?a{>6RI$Ux9FodCRF*my9h2@fN9#OeAK3<1pG{OnD%sX1$~u zQ$``|Hp5-002ovPDHLkV1hZ7oX!9M literal 0 HcmV?d00001 diff --git a/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error@2x.png b/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/error@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e7d831f060304abb297053cf712ac4c9e66e39 GIT binary patch literal 739 zcmV<90v!E`P)QrfzkMDd2JM^TJA_9(|&pzK>%t8pkh$WVq zN_}r#k4Yu9mU?MjlgWYzP=V~FZuGT{D8RGSPZC%KXrZapQn-^+KLg!I><<&iV11y; zqX2X9JjtQ`g{(ofwhE~Ud#-#*%}m#4WZ0Xz&jfQZl#t|m&_ko>OO64 z0aXK>`73-1a%>HzdXz$t4;>^JTQJcMB@`KSenS@Y#i2mG>)OPEd4O=utjnh?*tp=3 zWv#8*Yr!M1F%=ZA)Ie!n>Cdo2(cW<=*iM0AQ!x@EaYPu#N7^fZZKZv&C^UGEC*nX( z1jqEVpuzbcz!)*&Cc!QzfNNA>4A88>y`u%clX@Dtf70J^A^Y|kykSSk!jc^Dd&~^G zHB&T3=4J&jH{(b+jFb5_!kcN@(IXoj?Ct>5weQ*_pxz++m||VJu&@TRoZ6tiMFdqa zS1HwGDr8cHSO~hpD5XcFfCH5*9%)H3IBGx&NK!Z#fHsiiaIOHo5*L72XEPPnc>!qa zNy#B2kKmj@MnmPcC+>likd*?gJ%gi!tQFwpfp8qSh1fz~W}hz2KH-g8soZBUf*%qB%rfm2UgK#%N?TvVZ`y?$k8N2P=m!MyRun3Y+krJcpdjHcZc|rR?XZ#?qrTmzgB% zrX`E2yT5qgEsZJ>keW=b8-C_KdC+|@&o2HC#0Gw(dG?3WsQ|mM%IkXZw}Nj01^{^@ V%A!H|hk5`2002ovPDHLkV1h0;OXUCn literal 0 HcmV?d00001 diff --git a/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/success.png b/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.bundle/success.png new file mode 100644 index 0000000000000000000000000000000000000000..45424a94b57fc184166ab24b1ce18bdebf7e3553 GIT binary patch literal 508 zcmV8H>fLn^?viA{R(qpi; zFpSu*;f8DSD3&w^{f0}qC@o>GYxdkHk0Qg=BD;`f=YbE&yfn|y2d5T=vS4E);7aCZ z9=>Bym7ieD#zGJ&z6mnJK|{kk@Gi+trGZmw#3ss&?%7yG8LHHEY9_oATw_|&T&_lT zKXT}Zk?97U6IUABz|-*CWMv#zlsoUUk>NIwDb7CdD_(jM)1hQ6G1#-dYU97K{wO0f zFiXp=Z6aCkA$YF&CnJN(o|Idgs(B|B*_oU!Mf27TkVsC0Rg=@iqyQ2!at#Y5`Bztr yJ-7t(mc8Z_Yp;7RJ=itzPcOplLtpj31sDMDi+UKE_o6-k0000g=@&w(%155Qy??jJP4G0C1lG=!;IC$`&U&OQL!i` zq1=RTG%N{h{;6Qs4Q7cJ#BBM?kOEo_yB4New5MPyYh@H}rV3N@yN!(IWCY)cS@DDb zXDa63R3wviv{oa_uM#mOYtegDa8Ivk3xVy6?dQ;l8`cy@x3o zGmCK#Lz3ApI4$(G=h1T9Z4WRBAT#)+g?D5$Z0Gomi(7`BDlPnU96#ey{h}~X&^{3F z9X(pL7f!f5F~pK#dOltBp;YS#z(M5pi1%vWEkps()GY*UwmCucVx@&e0F0RyCha-E zUTOz)ZX$%(X<<^ba50rcpp!a6b_-~81(+LT{cGfIAv!?J*~K+-w}75#9RVQtcCefi z#QS-SCij0CRjmnc(c{uq#@=j4351rrC{g +#import + +enum { + SVProgressHUDMaskTypeNone = 1, // allow user interactions while HUD is displayed + SVProgressHUDMaskTypeClear, // don't allow + SVProgressHUDMaskTypeBlack, // don't allow and dim the UI in the back of the HUD + SVProgressHUDMaskTypeGradient // don't allow and dim the UI with a a-la-alert-view bg gradient +}; + +typedef NSUInteger SVProgressHUDMaskType; + +@interface SVProgressHUD : UIView + ++ (void)show; ++ (void)showWithStatus:(NSString*)status; ++ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType; ++ (void)showWithMaskType:(SVProgressHUDMaskType)maskType; + ++ (void)showSuccessWithStatus:(NSString*)string; ++ (void)showSuccessWithStatus:(NSString *)string duration:(NSTimeInterval)duration; ++ (void)showErrorWithStatus:(NSString *)string; ++ (void)showErrorWithStatus:(NSString *)string duration:(NSTimeInterval)duration; + ++ (void)setStatus:(NSString*)string; // change the HUD loading status while it's showing + ++ (void)dismiss; // simply dismiss the HUD with a fade+scale out animation ++ (void)dismissWithSuccess:(NSString*)successString; // also displays the success icon image ++ (void)dismissWithSuccess:(NSString*)successString afterDelay:(NSTimeInterval)seconds; ++ (void)dismissWithError:(NSString*)errorString; // also displays the error icon image ++ (void)dismissWithError:(NSString*)errorString afterDelay:(NSTimeInterval)seconds; + ++ (BOOL)isVisible; + +@end diff --git a/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.m b/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.m new file mode 100644 index 0000000..9c68402 --- /dev/null +++ b/Mooshimeter/Vendor/SVProgressHUD/SVProgressHUD.m @@ -0,0 +1,542 @@ +// +// SVProgressHUD.m +// +// Created by Sam Vermette on 27.03.11. +// Copyright 2011 Sam Vermette. All rights reserved. +// +// https://github.com/samvermette/SVProgressHUD +// + +#import "SVProgressHUD.h" +#import + +@interface SVProgressHUD () + +@property (nonatomic, readwrite) SVProgressHUDMaskType maskType; +@property (nonatomic, strong, readonly) NSTimer *fadeOutTimer; + +@property (nonatomic, strong, readonly) UIWindow *overlayWindow; +@property (nonatomic, strong, readonly) UIView *hudView; +@property (nonatomic, strong, readonly) UILabel *stringLabel; +@property (nonatomic, strong, readonly) UIImageView *imageView; +@property (nonatomic, strong, readonly) UIActivityIndicatorView *spinnerView; + +@property (nonatomic, readonly) CGFloat visibleKeyboardHeight; + +- (void)showWithStatus:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType networkIndicator:(BOOL)show; +- (void)setStatus:(NSString*)string; +- (void)registerNotifications; +- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle; +- (void)positionHUD:(NSNotification*)notification; + +- (void)dismiss; +- (void)dismissWithStatus:(NSString*)string error:(BOOL)error; +- (void)dismissWithStatus:(NSString*)string error:(BOOL)error afterDelay:(NSTimeInterval)seconds; + +@end + + +@implementation SVProgressHUD + +@synthesize overlayWindow, hudView, maskType, fadeOutTimer, stringLabel, imageView, spinnerView, visibleKeyboardHeight; + +- (void)dealloc { + self.fadeOutTimer = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + + ++ (SVProgressHUD*)sharedView { + static dispatch_once_t once; + static SVProgressHUD *sharedView; + dispatch_once(&once, ^ { sharedView = [[SVProgressHUD alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; }); + return sharedView; +} + + ++ (void)setStatus:(NSString *)string { + [[SVProgressHUD sharedView] setStatus:string]; +} + +#pragma mark - Show Methods + ++ (void)show { + [[SVProgressHUD sharedView] showWithStatus:nil maskType:SVProgressHUDMaskTypeNone networkIndicator:NO]; +} + ++ (void)showWithStatus:(NSString *)status { + [[SVProgressHUD sharedView] showWithStatus:status maskType:SVProgressHUDMaskTypeNone networkIndicator:NO]; +} + ++ (void)showWithMaskType:(SVProgressHUDMaskType)maskType { + [[SVProgressHUD sharedView] showWithStatus:nil maskType:maskType networkIndicator:NO]; +} + ++ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType { + [[SVProgressHUD sharedView] showWithStatus:status maskType:maskType networkIndicator:NO]; +} + ++ (void)showSuccessWithStatus:(NSString *)string { + [SVProgressHUD showSuccessWithStatus:string duration:1]; +} + ++ (void)showSuccessWithStatus:(NSString *)string duration:(NSTimeInterval)duration { + [SVProgressHUD show]; + [SVProgressHUD dismissWithSuccess:string afterDelay:duration]; +} + ++ (void)showErrorWithStatus:(NSString *)string { + [SVProgressHUD showErrorWithStatus:string duration:1]; +} + ++ (void)showErrorWithStatus:(NSString *)string duration:(NSTimeInterval)duration { + [SVProgressHUD show]; + [SVProgressHUD dismissWithError:string afterDelay:duration]; +} + + +#pragma mark - Dismiss Methods + ++ (void)dismiss { + [[SVProgressHUD sharedView] dismiss]; +} + ++ (void)dismissWithSuccess:(NSString*)successString { + [[SVProgressHUD sharedView] dismissWithStatus:successString error:NO]; +} + ++ (void)dismissWithSuccess:(NSString *)successString afterDelay:(NSTimeInterval)seconds { + [[SVProgressHUD sharedView] dismissWithStatus:successString error:NO afterDelay:seconds]; +} + ++ (void)dismissWithError:(NSString*)errorString { + [[SVProgressHUD sharedView] dismissWithStatus:errorString error:YES]; +} + ++ (void)dismissWithError:(NSString *)errorString afterDelay:(NSTimeInterval)seconds { + [[SVProgressHUD sharedView] dismissWithStatus:errorString error:YES afterDelay:seconds]; +} + + +#pragma mark - Instance Methods + +- (id)initWithFrame:(CGRect)frame { + + if ((self = [super initWithFrame:frame])) { + self.userInteractionEnabled = NO; + self.backgroundColor = [UIColor clearColor]; + self.alpha = 0; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + } + + return self; +} + +- (void)drawRect:(CGRect)rect { + + CGContextRef context = UIGraphicsGetCurrentContext(); + + switch (self.maskType) { + + case SVProgressHUDMaskTypeBlack: { + [[UIColor colorWithWhite:0 alpha:0.5] set]; + CGContextFillRect(context, self.bounds); + break; + } + + case SVProgressHUDMaskTypeGradient: { + + size_t locationsCount = 2; + CGFloat locations[2] = {0.0f, 1.0f}; + CGFloat colors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount); + CGColorSpaceRelease(colorSpace); + + CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); + float radius = MIN(self.bounds.size.width , self.bounds.size.height) ; + CGContextDrawRadialGradient (context, gradient, center, 0, center, radius, kCGGradientDrawsAfterEndLocation); + CGGradientRelease(gradient); + + break; + } + } +} + +- (void)setStatus:(NSString *)string { + + CGFloat hudWidth = 100; + CGFloat hudHeight = 100; + CGFloat stringWidth = 0; + CGFloat stringHeight = 0; + CGRect labelRect = CGRectZero; + + if(string) { + CGSize stringSize = [string sizeWithFont:self.stringLabel.font constrainedToSize:CGSizeMake(200, 300)]; + stringWidth = stringSize.width; + stringHeight = stringSize.height; + hudHeight = 80+stringHeight; + + if(stringWidth > hudWidth) + hudWidth = ceil(stringWidth/2)*2; + + if(hudHeight > 100) { + labelRect = CGRectMake(12, 66, hudWidth, stringHeight); + hudWidth+=24; + } else { + hudWidth+=24; + labelRect = CGRectMake(0, 66, hudWidth, stringHeight); + } + } + + self.hudView.bounds = CGRectMake(0, 0, hudWidth, hudHeight); + + if(string) + self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, 36); + else + self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2); + + self.stringLabel.hidden = NO; + self.stringLabel.text = string; + self.stringLabel.frame = labelRect; + + if(string) + self.spinnerView.center = CGPointMake(ceil(CGRectGetWidth(self.hudView.bounds)/2)+0.5, 40.5); + else + self.spinnerView.center = CGPointMake(ceil(CGRectGetWidth(self.hudView.bounds)/2)+0.5, ceil(self.hudView.bounds.size.height/2)+0.5); +} + +- (void)setFadeOutTimer:(NSTimer *)newTimer { + + if(fadeOutTimer) + [fadeOutTimer invalidate], fadeOutTimer = nil; + + if(newTimer) + fadeOutTimer = newTimer; +} + + +- (void)registerNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(positionHUD:) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(positionHUD:) + name:UIKeyboardWillHideNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(positionHUD:) + name:UIKeyboardDidHideNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(positionHUD:) + name:UIKeyboardWillShowNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(positionHUD:) + name:UIKeyboardDidShowNotification + object:nil]; +} + + +- (void)positionHUD:(NSNotification*)notification { + + CGFloat keyboardHeight; + double animationDuration; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + + if(notification) { + NSDictionary* keyboardInfo = [notification userInfo]; + CGRect keyboardFrame = [[keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; + animationDuration = [[keyboardInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + + if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) { + if(UIInterfaceOrientationIsPortrait(orientation)) + keyboardHeight = keyboardFrame.size.height; + else + keyboardHeight = keyboardFrame.size.width; + } else + keyboardHeight = 0; + } else { + keyboardHeight = self.visibleKeyboardHeight; + } + + CGRect orientationFrame = [UIScreen mainScreen].bounds; + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + + if(UIInterfaceOrientationIsLandscape(orientation)) { + float temp = orientationFrame.size.width; + orientationFrame.size.width = orientationFrame.size.height; + orientationFrame.size.height = temp; + + temp = statusBarFrame.size.width; + statusBarFrame.size.width = statusBarFrame.size.height; + statusBarFrame.size.height = temp; + } + + CGFloat activeHeight = orientationFrame.size.height; + + if(keyboardHeight > 0) + activeHeight += statusBarFrame.size.height*2; + + activeHeight -= keyboardHeight; + CGFloat posY = floor(activeHeight*0.45); + CGFloat posX = orientationFrame.size.width/2; + + CGPoint newCenter; + CGFloat rotateAngle; + + switch (orientation) { + case UIInterfaceOrientationPortraitUpsideDown: + rotateAngle = M_PI; + newCenter = CGPointMake(posX, orientationFrame.size.height-posY); + break; + case UIInterfaceOrientationLandscapeLeft: + rotateAngle = -M_PI/2.0f; + newCenter = CGPointMake(posY, posX); + break; + case UIInterfaceOrientationLandscapeRight: + rotateAngle = M_PI/2.0f; + newCenter = CGPointMake(orientationFrame.size.height-posY, posX); + break; + default: // as UIInterfaceOrientationPortrait + rotateAngle = 0.0; + newCenter = CGPointMake(posX, posY); + break; + } + + if(notification) { + [UIView animateWithDuration:animationDuration + delay:0 + options:UIViewAnimationOptionAllowUserInteraction + animations:^{ + [self moveToPoint:newCenter rotateAngle:rotateAngle]; + } completion:NULL]; + } + + else { + [self moveToPoint:newCenter rotateAngle:rotateAngle]; + } + +} + +- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle { + self.hudView.transform = CGAffineTransformMakeRotation(angle); + self.hudView.center = newCenter; +} + +#pragma mark - Master show/dismiss methods + +- (void)showWithStatus:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType networkIndicator:(BOOL)show { + dispatch_async(dispatch_get_main_queue(), ^{ + if(!self.superview) + [self.overlayWindow addSubview:self]; + + self.fadeOutTimer = nil; + self.imageView.hidden = YES; + self.maskType = hudMaskType; + + [self setStatus:string]; + [self.spinnerView startAnimating]; + + if(self.maskType != SVProgressHUDMaskTypeNone) { + self.overlayWindow.userInteractionEnabled = YES; + } else { + self.overlayWindow.userInteractionEnabled = NO; + } + + [self.overlayWindow makeKeyAndVisible]; + [self positionHUD:nil]; + + if(self.alpha != 1) { + [self registerNotifications]; + self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3); + + [UIView animateWithDuration:0.15 + delay:0 + options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState + animations:^{ + self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1/1.3, 1/1.3); + self.alpha = 1; + } + completion:NULL]; + } + + [self setNeedsDisplay]; + }); +} + + +- (void)dismissWithStatus:(NSString*)string error:(BOOL)error { + [self dismissWithStatus:string error:error afterDelay:0.9]; +} + + +- (void)dismissWithStatus:(NSString *)string error:(BOOL)error afterDelay:(NSTimeInterval)seconds { + dispatch_async(dispatch_get_main_queue(), ^{ + if(self.alpha != 1) + return; + + if(error) + self.imageView.image = [UIImage imageNamed:@"SVProgressHUD.bundle/error.png"]; + else + self.imageView.image = [UIImage imageNamed:@"SVProgressHUD.bundle/success.png"]; + + self.imageView.hidden = NO; + [self setStatus:string]; + [self.spinnerView stopAnimating]; + + self.fadeOutTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(dismiss) userInfo:nil repeats:NO]; + }); +} + +- (void)dismiss { + dispatch_async(dispatch_get_main_queue(), ^{ + + [UIView animateWithDuration:0.15 + delay:0 + options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction + animations:^{ + self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 0.8, 0.8); + self.alpha = 0; + } + completion:^(BOOL finished){ + if(self.alpha == 0) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [hudView removeFromSuperview]; + hudView = nil; + + // Make sure to remove the overlay window from the list of windows + // before trying to find the key window in that same list + NSMutableArray *windows = [[NSMutableArray alloc] initWithArray:[UIApplication sharedApplication].windows]; + [windows removeObject:overlayWindow]; + overlayWindow = nil; + + [windows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIWindow *window, NSUInteger idx, BOOL *stop) { + if([window isKindOfClass:[UIWindow class]] && window.windowLevel == UIWindowLevelNormal) { + [window makeKeyWindow]; + *stop = YES; + } + }]; + + // uncomment to make sure UIWindow is gone from app.windows + //NSLog(@"%@", [UIApplication sharedApplication].windows); + //NSLog(@"keyWindow = %@", [UIApplication sharedApplication].keyWindow); + } + }]; + }); +} + +#pragma mark - Utilities + ++ (BOOL)isVisible { + return ([SVProgressHUD sharedView].alpha == 1); +} + + +#pragma mark - Getters + +- (UIWindow *)overlayWindow { + if(!overlayWindow) { + overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + overlayWindow.backgroundColor = [UIColor clearColor]; + overlayWindow.userInteractionEnabled = NO; + } + return overlayWindow; +} + +- (UIView *)hudView { + if(!hudView) { + hudView = [[UIView alloc] initWithFrame:CGRectZero]; + hudView.layer.cornerRadius = 10; + hudView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8]; + hudView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin); + + [self addSubview:hudView]; + } + return hudView; +} + +- (UILabel *)stringLabel { + if (stringLabel == nil) { + stringLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + stringLabel.textColor = [UIColor whiteColor]; + stringLabel.backgroundColor = [UIColor clearColor]; + stringLabel.adjustsFontSizeToFitWidth = YES; + stringLabel.textAlignment = NSTextAlignmentCenter; + stringLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; + stringLabel.font = [UIFont boldSystemFontOfSize:16]; + stringLabel.shadowColor = [UIColor blackColor]; + stringLabel.shadowOffset = CGSizeMake(0, -1); + stringLabel.numberOfLines = 0; + } + + if(!stringLabel.superview) + [self.hudView addSubview:stringLabel]; + + return stringLabel; +} + +- (UIImageView *)imageView { + if (imageView == nil) + imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 28)]; + + if(!imageView.superview) + [self.hudView addSubview:imageView]; + + return imageView; +} + +- (UIActivityIndicatorView *)spinnerView { + if (spinnerView == nil) { + spinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + spinnerView.hidesWhenStopped = YES; + spinnerView.bounds = CGRectMake(0, 0, 37, 37); + } + + if(!spinnerView.superview) + [self.hudView addSubview:spinnerView]; + + return spinnerView; +} + +- (CGFloat)visibleKeyboardHeight { + + UIWindow *keyboardWindow = nil; + for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) { + if(![[testWindow class] isEqual:[UIWindow class]]) { + keyboardWindow = testWindow; + break; + } + } + + // Locate UIKeyboard. + UIView *foundKeyboard = nil; + for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) { + + // iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView. + if ([[possibleKeyboard description] hasPrefix:@" 100) + return foundKeyboard.bounds.size.height; + + return 0; +} + +@end diff --git a/Mooshimeter/self.iBytes += OAD_BLOC.textClipping b/Mooshimeter/self.iBytes += OAD_BLOC.textClipping new file mode 100644 index 0000000..e69de29 diff --git a/Provisioning/Mooshi.p12 b/Provisioning/Mooshi.p12 new file mode 100755 index 0000000000000000000000000000000000000000..dd7fe41302dd218d5f1a9a867e9c74b84063b472 GIT binary patch literal 3238 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 literal 0 HcmV?d00001 diff --git a/Provisioning/Mooshimeter_Development.mobileprovision b/Provisioning/Mooshimeter_Development.mobileprovision new file mode 100755 index 0000000000000000000000000000000000000000..f6ed09925a820e061e6846b8d0d40a7e0b01522e GIT binary patch literal 9466 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

E7zX+%-Soy9Dn`$I~>uf7kr*Z6@Oxxv3tpiBRY zM6E_Nq1S(5R)HAMhr zvV)Er_sqKe`0iIPy>r#C=GdOiwrKDM$k8)sD2zi|sJ{Qe>OXY)4ge9Q>vv5}O`in- z(0baUInaRWUv>tZ+&`^1?t#n>M2kM+vnOXyf2w9)i}uJ}j}7_YqxUuzCVc&4;E5?) z?%sTt&3E3)ZC@nUZAdQl{xa=HZS&gC#%%le^Ypt9KfM0Ajc2vTFMIC!mh`pfJ04iH zXxE~jZ#aIIa^2uzSDyCmK+E>qj?Fqga@_^VR>{=G9)FK^91nf_*d24fds`lF zzWDBki66I)T({+zqiNF5myEw{^Pq-f%MbKF^7dEz;2|&UzJ15K52s(a8dtVt|F+`G ztFL)x@Uu?GjN7I>iT?G9!);qzUOKijwRYo!9W%TSUe-D@_r|`aH@?iezV!CLY2vtD zy4X*<9*Hu^YtAe`vj4$@s<98vnUptAyt97Cu Date: Sat, 25 Apr 2015 05:28:06 +0800 Subject: [PATCH 2/9] add mail support fuction on settings menu --- Mooshimeter.xcodeproj/project.pbxproj | 4 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 104 +++++++++++++ .../info_icon.imageset/Contents.json | 21 +++ .../info_icon.imageset/info@2x.PNG | Bin 0 -> 759 bytes .../mail_icon.imageset/Contents.json | 21 +++ .../mail_icon.imageset/mail@2x.png | Bin 0 -> 713 bytes .../web_icon.imageset/Contents.json | 21 +++ .../web_icon.imageset/web@2x.PNG | Bin 0 -> 1714 bytes Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/ScanSettingsView.h | 13 ++ Mooshimeter/ScanSettingsView.m | 76 ++++++++- Mooshimeter/ScanViewController.h | 4 +- Mooshimeter/ScanViewController.m | 145 ++++++++++++++---- 13 files changed, 372 insertions(+), 39 deletions(-) create mode 100644 Mooshimeter/Images.xcassets/info_icon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/info_icon.imageset/info@2x.PNG create mode 100644 Mooshimeter/Images.xcassets/mail_icon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/mail_icon.imageset/mail@2x.png create mode 100644 Mooshimeter/Images.xcassets/web_icon.imageset/Contents.json create mode 100644 Mooshimeter/Images.xcassets/web_icon.imageset/web@2x.PNG diff --git a/Mooshimeter.xcodeproj/project.pbxproj b/Mooshimeter.xcodeproj/project.pbxproj index 4545467..b00bf49 100644 --- a/Mooshimeter.xcodeproj/project.pbxproj +++ b/Mooshimeter.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 847121961AE61AC400035BB2 /* SVProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 847121941AE61AC400035BB2 /* SVProgressHUD.m */; }; 847A13A11AE3B94900E36E17 /* KxMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 847A139E1AE3B94900E36E17 /* KxMenu.m */; }; 847A13A21AE3B94900E36E17 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = 847A13A01AE3B94900E36E17 /* UIImage+ImageEffects.m */; }; + 847A42241AE774FF00D4F00D /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 847A42231AE774FF00D4F00D /* MessageUI.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -222,6 +223,7 @@ 847A139E1AE3B94900E36E17 /* KxMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KxMenu.m; sourceTree = ""; }; 847A139F1AE3B94900E36E17 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ImageEffects.h"; sourceTree = ""; }; 847A13A01AE3B94900E36E17 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ImageEffects.m"; sourceTree = ""; }; + 847A42231AE774FF00D4F00D /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -229,6 +231,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 847A42241AE774FF00D4F00D /* MessageUI.framework in Frameworks */, 4ADDC5D51A882CFB00FC5093 /* Accelerate.framework in Frameworks */, 4ABE54161A07FF0000277456 /* CoreGraphics.framework in Frameworks */, 4ABE54151A07FEE900277456 /* Foundation.framework in Frameworks */, @@ -279,6 +282,7 @@ 0C63852B17D694DC00D3FB31 /* Frameworks */ = { isa = PBXGroup; children = ( + 847A42231AE774FF00D4F00D /* MessageUI.framework */, 4ADDC5D41A882CFB00FC5093 /* Accelerate.framework */, 0C78971F17E3762000D1439E /* QuartzCore.framework */, 0C7895C817D786A400D1439E /* CoreBluetooth.framework */, diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 5fcde47..09b0dc5 100644 --- a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -31,5 +31,109 @@ endingLineNumber = "23"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/Images.xcassets/info_icon.imageset/Contents.json b/Mooshimeter/Images.xcassets/info_icon.imageset/Contents.json new file mode 100644 index 0000000..e5c7c10 --- /dev/null +++ b/Mooshimeter/Images.xcassets/info_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "info@2x.PNG" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/info_icon.imageset/info@2x.PNG b/Mooshimeter/Images.xcassets/info_icon.imageset/info@2x.PNG new file mode 100644 index 0000000000000000000000000000000000000000..2511b5775e25f3223b7d27d6146b54f147e1b457 GIT binary patch literal 759 zcmV(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;eMv+?RCwC#+c8KSK@@=De^mtGKoEq3fN)?X2!bG_vJz~CfMA=}Dy<+Wq)BUa zRUpAY5VRCd45Tp-5abFWm0%fAJhU;KYzk-KSay@mBhh*64LJ%n?5h9WRBdGfzpx2{=Yjncz(KjM+RhDl8u)Ah8qF`Nuc3n90e23tI`34U z&cWlro6V?>Ql^zxhmB5P4s&@Em-pDX)Xpl3fv1mm1P3Y z3twxC2{*$>!9EtUfDbG@7f0j0a32S0!Htw}JFh{%RWq?8JiGeF*hmB3%JJ9GpxjOa zZWkEbHU-aE7(~)Lt*sAsCeD_$rnT(B^3}4*BHogP0kj9qv~(<)fM-3J;n@n@2R!Wo zxNZWT^Z;Bd+s?y36L7}^@Pi5X)&p?I1bpoQ_}T<~=>hoC1pLbb@NX0FPY=L9Ot0Z0 zpVog&uVH}i1qOdhI?xaNmFFG$qQ#RMyD|6*I4U6cNR8bX46s{3@W;MK9WKNVdhwT7 z>dCwi4{}>Aw!vasEg-jeA+A>uN>lRCi*Ds)W~~>s^q$_$Az!o3 z%9eU#1NVh#8?POlt(`! pI>%vwcu@U}rvg{t3S6Z4KLFQfuq5++d$Irk002ovPDHLkV1fcOM+*P| literal 0 HcmV?d00001 diff --git a/Mooshimeter/Images.xcassets/mail_icon.imageset/Contents.json b/Mooshimeter/Images.xcassets/mail_icon.imageset/Contents.json new file mode 100644 index 0000000..619a0ac --- /dev/null +++ b/Mooshimeter/Images.xcassets/mail_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "mail@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/mail_icon.imageset/mail@2x.png b/Mooshimeter/Images.xcassets/mail_icon.imageset/mail@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b5453bd69c1db4983f5dad19a6f3c9400e2a974e GIT binary patch literal 713 zcmV;)0yh1LP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;Pf0{URCwC#+RbXhKoAG;e;V-Q)oa0*@EHU_@I8b=p&$tRLcR4FdK3ymPX)nV z3O(6lPTND*661b}zC^6aO~haZ zfH#8m6}**T&#~znc%#8aN9#<54ptOJXY0~Xr6YrR@D->iBJJFmp_R8hn8c1vAeCsFw8A&qX2N$G`-r!5qMY&SdJt zuK<2JbY1ZY;3c#V&j6e{aE6ZnrlCvv-hhr(v}}u*YX3hLz$^rOFrae)Utj^8LcrXB zZhJ{zVgFWi82CJ~4BaMJij4(Uq1ys0u`$6iv=8_I;911M6Fk)w)0p5U+67#R%fGVr zx>9VbJnoZ~?J_o4hCW$fm*UtFfR%`m*tW7jA8oLyz`8=F!%hI~YWsF#8^ke{>7v4f zO$oL^H?e!k9B&4+6E-zihql4W(2E+{8A}0{p}!rlGV~n4hYywt+(eHZvFF&=(4`NS z63oR}DLt_=^jNGGo+G&OSjIN!r9+=41INS$qf>)ZU}flqEjlH*5{ab1dZSZ;DX?DX z6ktlMGdec74XhJ7Cb&(kH98hJ2doS|Z=z#>D=~2nSR*JG literal 0 HcmV?d00001 diff --git a/Mooshimeter/Images.xcassets/web_icon.imageset/Contents.json b/Mooshimeter/Images.xcassets/web_icon.imageset/Contents.json new file mode 100644 index 0000000..7ea9175 --- /dev/null +++ b/Mooshimeter/Images.xcassets/web_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "web@2x.PNG" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mooshimeter/Images.xcassets/web_icon.imageset/web@2x.PNG b/Mooshimeter/Images.xcassets/web_icon.imageset/web@2x.PNG new file mode 100644 index 0000000000000000000000000000000000000000..88fcdaa230bb95ad75005faadb4473171076668a GIT binary patch literal 1714 zcmV;j22J^iP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ?I7vi7RCwC#TX95XSs4GlX+5G9k48jmX)TZ867d+8B_ftqMC@P7GDJkg5JN<3 zSYmj@&}c2uh!|R}&5l{trZhICl+C0}UK*1!tK9vg-`T#-JNMpm-o3qMw)b(p>7H}X z`F`ho-}%1tecz;f-^Wd&Tex9V0bBw6KckOKFH@;hQsdVGXa(>kfF=ML09gRlY(E3A z1mFO`7J#4myh&nm{uy*Bk#>M;u7SV&CgbpTG25e4*Dil!9rF#xXtv!(suXBuKzMK`P0}i}zCiJkftZw9S&2 zD+OE)-~}gqD|RmpU`RWaUy-3SfOYL4(gFJb7I}zexa;!N9uhm+CM4xW80ffw(*Wi; z(WVf&Ml!3Lmv0Tb5(d}@aF6f%3~h^?OG(To-_He!cU-`Ead2e$&I7P(VD1%}s296o z1GqqErAFIj_3Vm;ZlWsq+Ci8I;4UFv(%ke1FsAf+Fu=J80_+3mW!p%=g7j;rx-wl| zNdcBR)i2CNtvJMb0sJKvrIGr$Spwi?wq?b;b&7e7;7NI5Jz_EHk^nr;0cV6RFH%$= z3Gr4U-XjhdzXZU(u*U|!h2F?W7xpp9Dojk)6fCn0nN*w z09Flv*V#7Bz8%zk>3((r_{}iyu+V2E+Fl&MlN{usSmCe>!QW)IichIjC4lTkTr`x! z^8?>ah5=5Q4k=y_KjrG50r&*K9(SrEgwYcKACu8l055s{d&w)`9)KMH3&K%p;3Cy> zK|QL;ZH{*sv3O4be9dt-+4nO5Tdvo``@(7050Ri}PWae<3?24)j|O!$YTm4N-V6p| z3yXNDWB!RSK~c0Kb}lZdF_tX)D&Bq+3+~{4otqVP)GK`~Sgf@2lx7mx;R#LB!X6W! zjV?R<1%H314(GIMTo7&X_wxjHSdFo5(KkaL&&adHU=_quwr^h8R2$k6CMhl{zj@!X zf1Ad0iwoeU5_NKqt6iM{;5v%vfuqi^Xc(LTuZ4YVprfcesw34n0Jh@!R>3_ zV>$P+#`BR2;C)RGb}Uylco$@Gtz$@gSNHIs^i2 z;aUQK*EAP1O8n=^Yfi&p1$dhiW^kU{J>f+=in0z@x+q$5xQIL@kFDf+%~glx-SQj$ zet%7Xvle|7Z`~U2)}B^UA)Xa7zC<&zMctGko($)z<$>ez_clfK5aEfl#6mo#AYQXW z5HFp;E;oe4wp9?%a|z-_0rm`G+7ZVv?Vt$LE*m_pWwuSS?^g!LKZo-a>7h37P|{&q z;G&GdH4xt(!?nE2_5tlKUdxbAfs2&ZLQ1g%@yZUWIMH`v_hhQ2PXyUd* zRYR0?6_-;j)xtjCL&B*R6~R=Z?TPpI4PqvfX%9*O+!vxc19Em5>6sdHn%+|Lc zJYCv=%Y`X6>Cy~$@-@uq(iWk&SC%eSx@$^^HwkezCB#RNOhSBBEOc2C;!1a1OCD+r zro{v}s^p4Db&U>^wPt6mqD(&xYR>&acO~i*h>&zK*k`fxO567W+Sf}u=Jtd z+w$SC>tPb43dCD+Q6TOa!bxQ-5KmS98&3sr1#kuMheZDl0N5qH!5JV!E&u=k07*qo IM6N<$g7y{_5&!@I literal 0 HcmV?d00001 diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index 3b9607c..faf023d 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 325 + 352 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/ScanSettingsView.h b/Mooshimeter/ScanSettingsView.h index 1fe3a87..d694fbb 100644 --- a/Mooshimeter/ScanSettingsView.h +++ b/Mooshimeter/ScanSettingsView.h @@ -18,7 +18,20 @@ #import +@protocol ScanSettingsViewDelegate +@required +-(void)handleSendSupporMail; +@end + @interface ScanSettingsView : UIView +{ + +} + @property (strong,nonatomic) UILabel* about_section; @property (strong,nonatomic) UIButton* help_button; +@property (strong,nonatomic) UIButton* mail_button; + +@property (strong,nonatomic) id delegate; + @end \ No newline at end of file diff --git a/Mooshimeter/ScanSettingsView.m b/Mooshimeter/ScanSettingsView.m index 15f935b..b4f6dcd 100644 --- a/Mooshimeter/ScanSettingsView.m +++ b/Mooshimeter/ScanSettingsView.m @@ -8,6 +8,8 @@ #import "ScanSettingsView.h" +#import + @implementation ScanSettingsView @@ -15,18 +17,56 @@ -(instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; self.userInteractionEnabled = YES; + // FIXME: - fixed icon size + const CGFloat fixedIconSize = 30; + const CGFloat fixedPadding = 10; + const CGFloat fixedOffsetX = fixedIconSize + fixedPadding * 2; + const CGFloat adjustedWidth = frame.size.width - fixedOffsetX; + // Lay out the controls - const int nrow = 2; - const int ncol = 1; + const NSInteger nrow = 3; + const NSInteger ncol = 1; - float h = frame.size.height/nrow; - float w = frame.size.width/ncol; + CGFloat h = frame.size.height/nrow; + CGFloat w = adjustedWidth / ncol; + +#define cg(nx,ny,nw,nh,ox,oy) CGRectMake(nx*w+ox,ny*h+oy,nw*w,nh*h) + self.about_section = [[UILabel alloc]initWithFrame:cg(0,0,1,1,fixedOffsetX,0)]; + self.help_button = [[UIButton alloc]initWithFrame:cg(0,1,1,1,fixedOffsetX,0)]; + self.mail_button = [[UIButton alloc]initWithFrame:cg(0,2,1,1,fixedOffsetX,0)]; +#undef cg + w = frame.size.width/ncol; #define cg(nx,ny,nw,nh) CGRectMake(nx*w,ny*h,nw*w,nh*h) - self.about_section = [[UILabel alloc]initWithFrame:cg(0,0,1,1)]; - self.help_button = [[UIButton alloc]initWithFrame:cg(0,1,1,1)]; + UIView* about_view = [[UIView alloc] initWithFrame:cg(0,0,1,1)]; + UIView* help_view = [[UIView alloc] initWithFrame:cg(0,1,1,1)]; + UIView* mail_view = [[UIView alloc] initWithFrame:cg(0,2,1,1)]; + + [[about_view layer] setBorderWidth:2]; + [[about_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + + [[help_view layer] setBorderWidth:2]; + [[help_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + + [[mail_view layer] setBorderWidth:2]; + [[mail_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + + // Add as subviews + [self addSubview:about_view]; + [self addSubview:help_view]; + [self addSubview:mail_view]; #undef cg + // Place suitable icons + // By Jianying Shi + const CGFloat fixedOffsetY = (h - fixedIconSize) / 2; + UIImageView *infoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"info_icon"]]; + [infoImage setFrame:CGRectMake(fixedPadding, fixedOffsetY, fixedIconSize, fixedIconSize)]; + UIImageView *webImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"web_icon"]]; + [webImage setFrame:CGRectMake(fixedPadding, fixedOffsetY + h, fixedIconSize, fixedIconSize)]; + UIImageView *mailImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mail_icon"]]; + [mailImage setFrame:CGRectMake(fixedPadding, fixedOffsetY + h * 2, fixedIconSize, fixedIconSize)]; + // Set properties NSString * appBuildString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; NSString * appVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; @@ -42,12 +82,28 @@ -(instancetype)initWithFrame:(CGRect)frame { [ self.help_button setTitle:@"Open Help Site" forState:UIControlStateNormal]; [ self.help_button.titleLabel setFont:[UIFont systemFontOfSize:30]]; [ self.help_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - [[self.help_button layer] setBorderWidth:2]; - [[self.help_button layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + //[[self.help_button layer] setBorderWidth:2]; + //[[self.help_button layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + + // Added by Jianying Shi + // 04/22/2015 + [ self.mail_button addTarget:self action:@selector(launchMail) forControlEvents:UIControlEventTouchUpInside]; + [ self.mail_button setTitle:@"Mail Support" forState:UIControlStateNormal]; + [ self.mail_button.titleLabel setFont:[UIFont systemFontOfSize:30]]; + [ self.mail_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + //[[self.mail_button layer] setBorderWidth:2]; + //[[self.mail_button layer] setBorderColor:[UIColor darkGrayColor].CGColor]; // Add as subviews [self addSubview:self.about_section]; [self addSubview:self.help_button]; + [self addSubview:self.mail_button]; + + // Add icons + // By Jianying Shi + [self addSubview: infoImage]; + [self addSubview: webImage]; + [self addSubview: mailImage]; [[self layer] setBorderWidth:5]; [[self layer] setBorderColor:[UIColor darkGrayColor].CGColor]; @@ -60,5 +116,9 @@ -(void)launchHelp { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://moosh.im/support/"]]; } +// By Jianying Shi +-(void)launchMail { + [_delegate handleSendSupporMail]; +} @end diff --git a/Mooshimeter/ScanViewController.h b/Mooshimeter/ScanViewController.h index 2ddb79d..3168cbe 100644 --- a/Mooshimeter/ScanViewController.h +++ b/Mooshimeter/ScanViewController.h @@ -18,6 +18,8 @@ along with this program. If not, see . #import #import +#import + #import "LGPeripheral.h" #import "MooshimeterDevice.h" #import "ScanTableViewCell.h" @@ -29,7 +31,7 @@ along with this program. If not, see . -(void)handleScanViewSelect:(LGPeripheral*)p; @end -@interface ScanViewController : UITableViewController +@interface ScanViewController : UITableViewController @property (strong,nonatomic) id delegate; @property (strong,nonatomic) NSArray* peripherals; diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 6628f02..09cda41 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -22,6 +22,8 @@ #import "Vendor/KxMenu/KxMenu.h" #import "Vendor/LDProgressView/LDProgressView.h" +#import + // Uncomment if you want a simulated meter to appear in the scan list #define SIMULATED_METER @@ -80,39 +82,13 @@ - (void)viewDidLoad [super viewDidLoad]; [self.tableView registerClass:[ScanTableViewCell class] forCellReuseIdentifier:@"Cell"]; - -#if 0 - NSLog(@"Creating refresh handler..."); - UIRefreshControl *rescan_control = [[UIRefreshControl alloc] init]; - [rescan_control addTarget:self.delegate action:@selector(handleScanViewRefreshRequest) forControlEvents:UIControlEventValueChanged]; - self.refreshControl = rescan_control; -#endif - + // Added by Jianying Shi // 04/19/2015 // -- Scan Mooshimeter button - // UIBarButtonItem* nav_scan_btn = [[UIBarButtonItem alloc] initWithTitle:@"Scan" style:UIBarButtonItemStylePlain target:self.delegate action:@selector(handleScanViewRefreshRequest)]; UIBarButtonItem* nav_scan_btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"reloadicon"] style:UIBarButtonItemStyleBordered target:self.delegate action:@selector(handleScanViewRefreshRequest)]; -#if 0 - // -- Scan settings button - UIButton *menu_button = [UIButton buttonWithType:UIButtonTypeSystem]; - menu_button.frame = CGRectMake(0.0, 0.0, 44, 30); - // [setting_button setTitle:@"Setting" forState : UIControlStateNormal]; - [menu_button setImage:[UIImage imageNamed:@"menuicon"] forState:UIControlStateNormal]; - - UILongPressGestureRecognizer *longpressScanSetting = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longpressScanSetting:)]; - [menu_button addGestureRecognizer:longpressScanSetting]; - - UIBarButtonItem* nav_menu_button = [[UIBarButtonItem alloc] initWithCustomView:menu_button]; - - NSArray* navButtonArray = [[NSArray alloc] initWithObjects:nav_scan_btn, nav_menu_button, nil]; - - if( self.navigationItem != nil ) - self.navigationItem.leftBarButtonItems = navButtonArray; -#endif - if( self.navigationItem != nil ) self.navigationItem.leftBarButtonItem = nav_scan_btn; } @@ -139,15 +115,17 @@ - (NSUInteger)supportedInterfaceOrientations { -(void)settings_button_press { if(!self.settings_view) { + CGFloat fixedHeight = 250; CGRect frame = self.view.frame; frame.origin.x += .05*frame.size.width; - frame.origin.y += (frame.size.height - 250)/2; + frame.origin.y += (frame.size.height - fixedHeight)/2; frame.size.width *= 0.9; - frame.size.height = 250; + frame.size.height = fixedHeight; ScanSettingsView* g = [[ScanSettingsView alloc] initWithFrame:frame]; [g setBackgroundColor:[UIColor whiteColor]]; [g setAlpha:0.9]; self.settings_view = g; + self.settings_view.delegate = self; } if([self.view.subviews containsObject:self.settings_view]) { [self.settings_view removeFromSuperview]; @@ -226,4 +204,113 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self.delegate handleScanViewSelect:c.p]; } +#pragma mark - ScanSettingViewDelegate + +- (NSString *)platformRawString { + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char *machine = malloc(size); + sysctlbyname("hw.machine", machine, &size, NULL, 0); + NSString *platform = [NSString stringWithUTF8String:machine]; + free(machine); + return platform; +} + +- (NSString *)platformNiceString { + NSString *platform = [self platformRawString]; + if ([platform isEqualToString:@"iPhone1,1"]) return @"iPhone 1G"; + if ([platform isEqualToString:@"iPhone1,2"]) return @"iPhone 3G"; + if ([platform isEqualToString:@"iPhone2,1"]) return @"iPhone 3GS"; + if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone 4"; + if ([platform isEqualToString:@"iPhone3,3"]) return @"Verizon iPhone 4"; + if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone 4S"; + if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone 5"; + if ([platform isEqualToString:@"iPod1,1"]) return @"iPod Touch 1G"; + if ([platform isEqualToString:@"iPod2,1"]) return @"iPod Touch 2G"; + if ([platform isEqualToString:@"iPod3,1"]) return @"iPod Touch 3G"; + if ([platform isEqualToString:@"iPod4,1"]) return @"iPod Touch 4G"; + if ([platform isEqualToString:@"iPad1,1"]) return @"iPad 1"; + if ([platform isEqualToString:@"iPad2,1"]) return @"iPad 2 (WiFi)"; + if ([platform isEqualToString:@"iPad2,2"]) return @"iPad 2 (GSM)"; + if ([platform isEqualToString:@"iPad2,3"]) return @"iPad 2 (CDMA)"; + if ([platform isEqualToString:@"iPad3,1"]) return @"iPad 3 (WiFi)"; + if ([platform isEqualToString:@"iPad3,2"]) return @"iPad 3 (4G,2)"; + if ([platform isEqualToString:@"iPad3,3"]) return @"iPad 3 (4G,3)"; + if ([platform isEqualToString:@"i386"]) return @"Simulator"; + if ([platform isEqualToString:@"x86_64"]) return @"Simulator"; + return platform; +} + +- (void) handleSendSupporMail +{ + + if( [MFMailComposeViewController canSendMail] ) + { + MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init]; + mail.mailComposeDelegate = self; + [mail setSubject:@"Support Request"]; + + // Compose Message Body + NSString *iOSVersion = [[UIDevice currentDevice] systemVersion]; + NSString *devLabel = [self platformRawString]; + + NSString* hardwareVersion = [NSString stringWithFormat:@"iOS version : %@ v%@\n", devLabel, iOSVersion]; + NSString* bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + NSString* appVersionString = [NSString stringWithFormat:@"Mooshimeter iOS App Version = %@\n", bundleVersion]; + NSString* mailBody = [hardwareVersion stringByAppendingString:appVersionString]; + + [mail setMessageBody:mailBody isHTML:NO]; + [mail setToRecipients:@[@"hello@moosh.im"]]; + + [self presentViewController:mail animated:YES completion:nil]; + } + else{ + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"This device can not send mail." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + } +} + +- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error +{ + switch (result) { + case MFMailComposeResultSent: + { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"Mail sent." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + NSLog(@"You sent the email."); + } + break; + case MFMailComposeResultSaved: + { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"Saved a draft of this email." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + NSLog(@"You saved a draft of this email"); + } + break; + case MFMailComposeResultCancelled: + { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"Cancelled sending this email." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + NSLog(@"You cancelled sending this email."); + } + break; + case MFMailComposeResultFailed: + { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"An error occurred when trying to compose this email." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + NSLog(@"Mail failed: An error occurred when trying to compose this email"); + } + break; + default: + { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Support Mail" message:@"An error occurred when trying to compose this email." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + NSLog(@"An error occurred when trying to compose this email"); + } + break; + } + + [self dismissViewControllerAnimated:YES completion:NULL]; +} + @end From 594ab55b73d216ea6c94a0e2ed5440dd812b5160 Mon Sep 17 00:00:00 2001 From: ganggangwang Date: Mon, 27 Apr 2015 10:00:01 +0800 Subject: [PATCH 3/9] make settings menu round rect, add long-press menu on each tableview cell. --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 48 ++++--- Mooshimeter/MeterViewController.m | 105 +------------- Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/ScanSettingsView.m | 38 +---- Mooshimeter/ScanViewController.h | 2 +- Mooshimeter/ScanViewController.m | 134 +++++++++++++++++- 6 files changed, 174 insertions(+), 155 deletions(-) diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 09b0dc5..f71b5c7 100644 --- a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -31,22 +31,6 @@ endingLineNumber = "23"> - - - - + + + + + + NSFoundationVersionNumber_iOS_6_1) { - settingsMenu.blurredBackground = YES; - } - - settingsMenu.tintColor = [UIColor darkGrayColor];//[[UIColor whiteColor] colorWithAlphaComponent:0.5f]; - settingsMenu.tintColor1 = [UIColor lightGrayColor]; - - settingsMenu.selectedColor = [UIColor colorWithRed:0.9f green:0 blue:0 alpha:1.f]; - settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; - } - - // Display check ID - NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; - NSString* deviceUUID = [g_meter.p UUIDString]; - - KxMenuItem* checkItem = settingsMenu.menuItems[2]; - if( savedUUID == nil || [savedUUID isEqualToString:deviceUUID] == NO ) - { - checkItem.image = nil; - } - else{ - checkItem.image = [UIImage imageNamed:@"checkicon"]; - } - - UIView* contentView = (UIView*) [sender valueForKey:@"view"]; - CGRect buttonRect = contentView.frame; - buttonRect.origin.y += buttonRect.size.height; - - [settingsMenu setTag:kContextMenu_Tag]; - [settingsMenu showMenuInView:self.view - fromRect:buttonRect]; -} - -- (void) firmwareUpdatePressed : (id) sender -{ - NSLog(@"Firmware Update"); - - [self.delegate updateFirmwareIfNeeded]; -} - -- (void) autoConnectPressed : (id) sender -{ - NSLog(@"Auto connect"); - - NSString* deviceUUID = [g_meter.p UUIDString]; - if( deviceUUID ) - [[NSUserDefaults standardUserDefaults] setObject:deviceUUID forKey:@"autoUUID"]; -} - -- (NSArray*) createSettingsMenuItems { - NSArray *menuItems = - @[ - - [KxMenuItem menuItem:@"Setting" - image:nil - target:nil - action:NULL], - - [KxMenuItem menuItem:@"Firmware Update" - image:nil - target:self - action:@selector(firmwareUpdatePressed:)], - - [KxMenuItem menuItem:@"Auto Connect" - image:[UIImage imageNamed:@"checkicon"] - target:self - action:@selector(autoConnectPressed:)], - ]; - - KxMenuItem *first = menuItems[0]; - first.foreColor = [UIColor colorWithRed:47/255.0f green:112/255.0f blue:225/255.0f alpha:1.0]; - first.alignment = NSTextAlignmentCenter; - - return menuItems; + [self.navigationController popViewControllerAnimated:YES]; } #pragma mark - View lifey cycle diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index faf023d..a8e851f 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 352 + 445 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/ScanSettingsView.m b/Mooshimeter/ScanSettingsView.m index b4f6dcd..09da11e 100644 --- a/Mooshimeter/ScanSettingsView.m +++ b/Mooshimeter/ScanSettingsView.m @@ -36,32 +36,12 @@ -(instancetype)initWithFrame:(CGRect)frame { self.mail_button = [[UIButton alloc]initWithFrame:cg(0,2,1,1,fixedOffsetX,0)]; #undef cg - w = frame.size.width/ncol; -#define cg(nx,ny,nw,nh) CGRectMake(nx*w,ny*h,nw*w,nh*h) - UIView* about_view = [[UIView alloc] initWithFrame:cg(0,0,1,1)]; - UIView* help_view = [[UIView alloc] initWithFrame:cg(0,1,1,1)]; - UIView* mail_view = [[UIView alloc] initWithFrame:cg(0,2,1,1)]; - - [[about_view layer] setBorderWidth:2]; - [[about_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; - - [[help_view layer] setBorderWidth:2]; - [[help_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; - - [[mail_view layer] setBorderWidth:2]; - [[mail_view layer] setBorderColor:[UIColor darkGrayColor].CGColor]; - - // Add as subviews - [self addSubview:about_view]; - [self addSubview:help_view]; - [self addSubview:mail_view]; -#undef cg - // Place suitable icons // By Jianying Shi const CGFloat fixedOffsetY = (h - fixedIconSize) / 2; UIImageView *infoImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"info_icon"]]; [infoImage setFrame:CGRectMake(fixedPadding, fixedOffsetY, fixedIconSize, fixedIconSize)]; + UIImageView *webImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"web_icon"]]; [webImage setFrame:CGRectMake(fixedPadding, fixedOffsetY + h, fixedIconSize, fixedIconSize)]; UIImageView *mailImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mail_icon"]]; @@ -75,24 +55,20 @@ -(instancetype)initWithFrame:(CGRect)frame { [self.about_section setText:about_string]; [self.about_section setFont:[UIFont systemFontOfSize:20]]; [self.about_section setTextAlignment:NSTextAlignmentCenter]; - [self.about_section setTextColor:[UIColor darkGrayColor]]; + [self.about_section setTextColor:[UIColor whiteColor]]; self.about_section.numberOfLines = 3; [ self.help_button addTarget:self action:@selector(launchHelp) forControlEvents:UIControlEventTouchUpInside]; [ self.help_button setTitle:@"Open Help Site" forState:UIControlStateNormal]; - [ self.help_button.titleLabel setFont:[UIFont systemFontOfSize:30]]; - [ self.help_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - //[[self.help_button layer] setBorderWidth:2]; - //[[self.help_button layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + [ self.help_button.titleLabel setFont:[UIFont systemFontOfSize:24]]; + [ self.help_button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; // Added by Jianying Shi // 04/22/2015 [ self.mail_button addTarget:self action:@selector(launchMail) forControlEvents:UIControlEventTouchUpInside]; [ self.mail_button setTitle:@"Mail Support" forState:UIControlStateNormal]; - [ self.mail_button.titleLabel setFont:[UIFont systemFontOfSize:30]]; - [ self.mail_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - //[[self.mail_button layer] setBorderWidth:2]; - //[[self.mail_button layer] setBorderColor:[UIColor darkGrayColor].CGColor]; + [ self.mail_button.titleLabel setFont:[UIFont systemFontOfSize:24]]; + [ self.mail_button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; // Add as subviews [self addSubview:self.about_section]; @@ -105,8 +81,6 @@ -(instancetype)initWithFrame:(CGRect)frame { [self addSubview: webImage]; [self addSubview: mailImage]; - [[self layer] setBorderWidth:5]; - [[self layer] setBorderColor:[UIColor darkGrayColor].CGColor]; return self; } diff --git a/Mooshimeter/ScanViewController.h b/Mooshimeter/ScanViewController.h index 3168cbe..966ac35 100644 --- a/Mooshimeter/ScanViewController.h +++ b/Mooshimeter/ScanViewController.h @@ -31,7 +31,7 @@ along with this program. If not, see . -(void)handleScanViewSelect:(LGPeripheral*)p; @end -@interface ScanViewController : UITableViewController +@interface ScanViewController : UITableViewController @property (strong,nonatomic) id delegate; @property (strong,nonatomic) NSArray* peripherals; diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 09cda41..3899adb 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -23,6 +23,8 @@ #import "Vendor/LDProgressView/LDProgressView.h" #import +#include +#include // Uncomment if you want a simulated meter to appear in the scan list #define SIMULATED_METER @@ -85,12 +87,20 @@ - (void)viewDidLoad // Added by Jianying Shi // 04/19/2015 - + // -- Scan Mooshimeter button UIBarButtonItem* nav_scan_btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"reloadicon"] style:UIBarButtonItemStyleBordered target:self.delegate action:@selector(handleScanViewRefreshRequest)]; if( self.navigationItem != nil ) self.navigationItem.leftBarButtonItem = nav_scan_btn; + + // Long-press table-view handler + UILongPressGestureRecognizer *longpressScanSetting = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longpressScanSetting:)]; + + longpressScanSetting.minimumPressDuration = 2.0; + longpressScanSetting.delegate = self; + + [self.tableView addGestureRecognizer:longpressScanSetting]; } #pragma mark - View lifecycle @@ -113,6 +123,121 @@ - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } +- (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer +{ + // Check if menu is currently showing. + UIView* existMenu = [self.view viewWithTag:kContextMenu_Tag]; + if( existMenu != nil ) + return; + + if (!settingsMenu) { + + settingsMenu = [KxMenu new]; + settingsMenu.menuItems = self.createSettingsMenuItems; + settingsMenu.blurredBackground = NO; + + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { + settingsMenu.blurredBackground = YES; + } + + settingsMenu.tintColor = [UIColor darkGrayColor];//[[UIColor whiteColor] colorWithAlphaComponent:0.5f]; + settingsMenu.tintColor1 = [UIColor lightGrayColor]; + + settingsMenu.selectedColor = [UIColor colorWithRed:0.9f green:0 blue:0 alpha:1.f]; + settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; + } + + // Display check ID +#if 0 + NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; + NSString* deviceUUID = [g_meter.p UUIDString]; + + KxMenuItem* checkItem = settingsMenu.menuItems[2]; + if( savedUUID == nil || [savedUUID isEqualToString:deviceUUID] == NO ) + { + checkItem.image = nil; + } + else{ + checkItem.image = [UIImage imageNamed:@"checkicon"]; + } +#endif + + // Check tapped table view cell + CGPoint p = [gestureRecognizer locationInView:self.tableView]; + NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p]; + if (indexPath == nil) { + NSLog(@"long press on table view but not on a row"); + } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { + + // UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath]; + // CGRect buttonRect = cell.frame; + + // Adjust button rect origin + // buttonRect.origin = p; + // buttonRect.origin.y += buttonRect.size.height; + + CGRect boundRect = CGRectMake(p.x, p.y, 5, 5); + boundRect.origin.y += 64; + + [settingsMenu setTag:kContextMenu_Tag]; + [settingsMenu showMenuInView:self.tableView + fromRect:boundRect]; + } else { + NSLog(@"gestureRecognizer.state = %d", gestureRecognizer.state); + } +} + + +- (NSArray*) createSettingsMenuItems { + NSArray *menuItems = + @[ + + [KxMenuItem menuItem:@"Setting" + image:nil + target:nil + action:NULL], + + [KxMenuItem menuItem:@"Firmware Update" + image:nil + target:self + action:@selector(firmwareUpdatePressed:)], + + [KxMenuItem menuItem:@"Auto Connect" + image:[UIImage imageNamed:@"checkicon"] + target:self + action:@selector(autoConnectPressed:)], + ]; + + KxMenuItem *first = menuItems[0]; + first.foreColor = [UIColor colorWithRed:47/255.0f green:112/255.0f blue:225/255.0f alpha:1.0]; + first.alignment = NSTextAlignmentCenter; + + return menuItems; +} + +#pragma mark - Menu Item Press Handler + +- (void) firmwareUpdatePressed : (id) sender +{ + NSLog(@"Firmware Update"); + +#if 0 + [self.delegate updateFirmwareIfNeeded]; +#endif +} + +- (void) autoConnectPressed : (id) sender +{ + NSLog(@"Auto connect"); + +#if 0 + NSString* deviceUUID = [g_meter.p UUIDString]; + if( deviceUUID ) + [[NSUserDefaults standardUserDefaults] setObject:deviceUUID forKey:@"autoUUID"]; +#endif + +} + -(void)settings_button_press { if(!self.settings_view) { CGFloat fixedHeight = 250; @@ -122,8 +247,13 @@ -(void)settings_button_press { frame.size.width *= 0.9; frame.size.height = fixedHeight; ScanSettingsView* g = [[ScanSettingsView alloc] initWithFrame:frame]; - [g setBackgroundColor:[UIColor whiteColor]]; + [g setBackgroundColor:[UIColor darkGrayColor]]; [g setAlpha:0.9]; + + // Make round rect of settings view + g.layer.cornerRadius = 25; + g.layer.masksToBounds = YES; + self.settings_view = g; self.settings_view.delegate = self; } From 9e5671522dbcb123f5b17297cc5f812bef8318d3 Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Sun, 3 May 2015 14:15:49 +0800 Subject: [PATCH 4/9] added signal strength view --- Mooshimeter.xcodeproj/project.pbxproj | 45 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 806 +++++++++++++++++- Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/ScanViewController.m | 49 +- Mooshimeter/Vendor/MPPlot/MPBarsGraphView.h | 21 + Mooshimeter/Vendor/MPPlot/MPBarsGraphView.m | 146 ++++ Mooshimeter/Vendor/MPPlot/MPGraphView.h | 20 + Mooshimeter/Vendor/MPPlot/MPGraphView.m | 225 +++++ Mooshimeter/Vendor/MPPlot/MPPlot.h | 90 ++ Mooshimeter/Vendor/MPPlot/MPPlot.m | 300 +++++++ .../Vendor/MPPlot/UIBezierPath+curved.h | 15 + .../Vendor/MPPlot/UIBezierPath+curved.m | 91 ++ Mooshimeter/Vendor/MPPlot/UIView+Frame.h | 15 + Mooshimeter/Vendor/MPPlot/UIView+Frame.m | 61 ++ Mooshimeter/Vendor/MPPlot/_MPWButton.h | 15 + Mooshimeter/Vendor/MPPlot/_MPWButton.m | 19 + .../PDChart/PDBarChart/PDBarChart.swift | 229 +++++ Mooshimeter/Vendor/PDChart/PDChart.swift | 36 + .../PDChartAxesComponent.swift | 254 ++++++ .../PDChart/PDLineChart/PDLineChart.swift | 158 ++++ .../PDChart/PDPieChart/PDPieChart.swift | 179 ++++ 21 files changed, 2759 insertions(+), 17 deletions(-) create mode 100644 Mooshimeter/Vendor/MPPlot/MPBarsGraphView.h create mode 100644 Mooshimeter/Vendor/MPPlot/MPBarsGraphView.m create mode 100644 Mooshimeter/Vendor/MPPlot/MPGraphView.h create mode 100644 Mooshimeter/Vendor/MPPlot/MPGraphView.m create mode 100644 Mooshimeter/Vendor/MPPlot/MPPlot.h create mode 100644 Mooshimeter/Vendor/MPPlot/MPPlot.m create mode 100644 Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.h create mode 100644 Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.m create mode 100644 Mooshimeter/Vendor/MPPlot/UIView+Frame.h create mode 100644 Mooshimeter/Vendor/MPPlot/UIView+Frame.m create mode 100644 Mooshimeter/Vendor/MPPlot/_MPWButton.h create mode 100644 Mooshimeter/Vendor/MPPlot/_MPWButton.m create mode 100644 Mooshimeter/Vendor/PDChart/PDBarChart/PDBarChart.swift create mode 100644 Mooshimeter/Vendor/PDChart/PDChart.swift create mode 100644 Mooshimeter/Vendor/PDChart/PDChartComponent/PDChartAxesComponent.swift create mode 100644 Mooshimeter/Vendor/PDChart/PDLineChart/PDLineChart.swift create mode 100644 Mooshimeter/Vendor/PDChart/PDPieChart/PDPieChart.swift diff --git a/Mooshimeter.xcodeproj/project.pbxproj b/Mooshimeter.xcodeproj/project.pbxproj index b00bf49..f008990 100644 --- a/Mooshimeter.xcodeproj/project.pbxproj +++ b/Mooshimeter.xcodeproj/project.pbxproj @@ -48,6 +48,12 @@ 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF4D8DC1A2BCD2600622F58 /* MeterSettingsView.m */; }; 842D9F641AE423B300ABBB89 /* LDProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F631AE423B200ABBB89 /* LDProgressView.m */; }; 842D9F671AE424ED00ABBB89 /* UIColor+RGBValues.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */; }; + 846403F21AF5603A002C78FF /* _MPWButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403E71AF5603A002C78FF /* _MPWButton.m */; }; + 846403F31AF5603A002C78FF /* MPBarsGraphView.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403E91AF5603A002C78FF /* MPBarsGraphView.m */; }; + 846403F41AF5603A002C78FF /* MPGraphView.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403EB1AF5603A002C78FF /* MPGraphView.m */; }; + 846403F51AF5603A002C78FF /* MPPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403ED1AF5603A002C78FF /* MPPlot.m */; }; + 846403F61AF5603A002C78FF /* UIBezierPath+curved.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403EF1AF5603A002C78FF /* UIBezierPath+curved.m */; }; + 846403F71AF5603A002C78FF /* UIView+Frame.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403F11AF5603A002C78FF /* UIView+Frame.m */; }; 847121951AE61AC400035BB2 /* SVProgressHUD.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 847121921AE61AC400035BB2 /* SVProgressHUD.bundle */; }; 847121961AE61AC400035BB2 /* SVProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 847121941AE61AC400035BB2 /* SVProgressHUD.m */; }; 847A13A11AE3B94900E36E17 /* KxMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 847A139E1AE3B94900E36E17 /* KxMenu.m */; }; @@ -216,6 +222,18 @@ 842D9F631AE423B200ABBB89 /* LDProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDProgressView.m; sourceTree = ""; }; 842D9F651AE424ED00ABBB89 /* UIColor+RGBValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+RGBValues.h"; sourceTree = ""; }; 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+RGBValues.m"; sourceTree = ""; }; + 846403E61AF5603A002C78FF /* _MPWButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _MPWButton.h; sourceTree = ""; }; + 846403E71AF5603A002C78FF /* _MPWButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _MPWButton.m; sourceTree = ""; }; + 846403E81AF5603A002C78FF /* MPBarsGraphView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPBarsGraphView.h; sourceTree = ""; }; + 846403E91AF5603A002C78FF /* MPBarsGraphView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPBarsGraphView.m; sourceTree = ""; }; + 846403EA1AF5603A002C78FF /* MPGraphView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGraphView.h; sourceTree = ""; }; + 846403EB1AF5603A002C78FF /* MPGraphView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGraphView.m; sourceTree = ""; }; + 846403EC1AF5603A002C78FF /* MPPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPlot.h; sourceTree = ""; }; + 846403ED1AF5603A002C78FF /* MPPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPlot.m; sourceTree = ""; }; + 846403EE1AF5603A002C78FF /* UIBezierPath+curved.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBezierPath+curved.h"; sourceTree = ""; }; + 846403EF1AF5603A002C78FF /* UIBezierPath+curved.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBezierPath+curved.m"; sourceTree = ""; }; + 846403F01AF5603A002C78FF /* UIView+Frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Frame.h"; sourceTree = ""; }; + 846403F11AF5603A002C78FF /* UIView+Frame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Frame.m"; sourceTree = ""; }; 847121921AE61AC400035BB2 /* SVProgressHUD.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SVProgressHUD.bundle; sourceTree = ""; }; 847121931AE61AC400035BB2 /* SVProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVProgressHUD.h; sourceTree = ""; }; 847121941AE61AC400035BB2 /* SVProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SVProgressHUD.m; sourceTree = ""; }; @@ -549,6 +567,26 @@ path = Mooshimeter/Vendor/LDProgressView; sourceTree = SOURCE_ROOT; }; + 846403E51AF5603A002C78FF /* MPPlot */ = { + isa = PBXGroup; + children = ( + 846403E61AF5603A002C78FF /* _MPWButton.h */, + 846403E71AF5603A002C78FF /* _MPWButton.m */, + 846403E81AF5603A002C78FF /* MPBarsGraphView.h */, + 846403E91AF5603A002C78FF /* MPBarsGraphView.m */, + 846403EA1AF5603A002C78FF /* MPGraphView.h */, + 846403EB1AF5603A002C78FF /* MPGraphView.m */, + 846403EC1AF5603A002C78FF /* MPPlot.h */, + 846403ED1AF5603A002C78FF /* MPPlot.m */, + 846403EE1AF5603A002C78FF /* UIBezierPath+curved.h */, + 846403EF1AF5603A002C78FF /* UIBezierPath+curved.m */, + 846403F01AF5603A002C78FF /* UIView+Frame.h */, + 846403F11AF5603A002C78FF /* UIView+Frame.m */, + ); + name = MPPlot; + path = Mooshimeter/Vendor/MPPlot; + sourceTree = SOURCE_ROOT; + }; 847121911AE61AC400035BB2 /* SVProgressHUD */ = { isa = PBXGroup; children = ( @@ -563,6 +601,7 @@ 847A13941AE3B27800E36E17 /* Vendor */ = { isa = PBXGroup; children = ( + 846403E51AF5603A002C78FF /* MPPlot */, 847121911AE61AC400035BB2 /* SVProgressHUD */, 842D9F611AE423B200ABBB89 /* LDProgressView */, 847A139C1AE3B94900E36E17 /* KxMenu */, @@ -711,6 +750,8 @@ buildActionMask = 2147483647; files = ( 4AA9EA301A12B54E0031C12B /* LGService.m in Sources */, + 846403F31AF5603A002C78FF /* MPBarsGraphView.m in Sources */, + 846403F51AF5603A002C78FF /* MPPlot.m in Sources */, 4A6716D71A8AC85E000A4F52 /* ScanSettingsView.m in Sources */, 4AA9EA2D1A12B54E0031C12B /* LGCentralManager.m in Sources */, 0C63853917D694DC00D3FB31 /* main.m in Sources */, @@ -721,11 +762,15 @@ 4A5F55581AB784B100212D1D /* MooshimeterDeviceSimulator.m in Sources */, 847A13A21AE3B94900E36E17 /* UIImage+ImageEffects.m in Sources */, 4ACF8AE11A0C918B00EC402B /* SmartNavigationController.m in Sources */, + 846403F41AF5603A002C78FF /* MPGraphView.m in Sources */, 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */, 0C63853D17D694DC00D3FB31 /* AppDelegate.m in Sources */, + 846403F61AF5603A002C78FF /* UIBezierPath+curved.m in Sources */, 0C63854C17D694DC00D3FB31 /* ScanViewController.m in Sources */, + 846403F21AF5603A002C78FF /* _MPWButton.m in Sources */, 842D9F641AE423B300ABBB89 /* LDProgressView.m in Sources */, 4AA9EA2F1A12B54E0031C12B /* LGPeripheral.m in Sources */, + 846403F71AF5603A002C78FF /* UIView+Frame.m in Sources */, 4AA9EA2E1A12B54E0031C12B /* LGCharacteristic.m in Sources */, 0C63854F17D694DC00D3FB31 /* MeterViewController.m in Sources */, 4AA9EA311A12B54E0031C12B /* LGUtils.m in Sources */, diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index f71b5c7..19a528f 100644 --- a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -112,7 +112,7 @@ moduleName = "Mooshimeter" usesParentBreakpointCondition = "Yes" urlString = "file:///Users/admin/Desktop/Elance%20Job/Mooshimeter/LGBluetooth/LGUtils.m" - timestampString = "451792391.383066" + timestampString = "452314436.875006" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "181" @@ -127,7 +127,7 @@ moduleName = "Mooshimeter" usesParentBreakpointCondition = "Yes" urlString = "file:///Users/admin/Desktop/Elance%20Job/Mooshimeter/LGBluetooth/LGUtils.m" - timestampString = "451792391.383451" + timestampString = "452314436.87525" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "181" @@ -151,5 +151,807 @@ endingLineNumber = "266"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index a8e851f..3d0ec1b 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 445 + 520 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 3899adb..ea799f4 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -22,6 +22,12 @@ #import "Vendor/KxMenu/KxMenu.h" #import "Vendor/LDProgressView/LDProgressView.h" +// Bar chart +// Jianying Shi. 05/02/2015 +#import "Vendor/MPPlot/MPPlot.h" +#import "Vendor/MPPlot/MPGraphView.h" +#import "Vendor/MPPlot/MPBarsGraphView.h" + #import #include #include @@ -32,6 +38,8 @@ #define kRSSIView_Tag 1000 #define kContextMenu_Tag 2000 +#define Round(a) (NSInteger) (a + 0.5) + @interface ScanViewController () { NSMutableArray *_objects; @@ -295,26 +303,39 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N ScanTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; // Add progress view to show signal strength - // By Jianying Shi. 04/19/2015 + // Updated by Jianying Shi. 05/02/2015 // Detect if progress view has already created. - LDProgressView* viewForCell = (LDProgressView*) [cell viewWithTag:indexPath.row + kRSSIView_Tag]; - if( viewForCell == nil ) // Already create - { + MPBarsGraphView* chartView = (MPBarsGraphView*) [cell viewWithTag:indexPath.row + kRSSIView_Tag]; + if( chartView == nil ) { // Not yet created + CGRect cellBounds = [cell frame]; - CGRect progressRect = CGRectMake(cellBounds.size.width * 0.70f, cellBounds.size.height / 2 - 5, cellBounds.size.width * 0.25f, 10); - LDProgressView* rssi_view = [[LDProgressView alloc] initWithFrame:progressRect]; //[[LDProgressView alloc] initWithProgressViewStyle : UIProgressViewStyleDefault]; - [rssi_view setTag:indexPath.row + kRSSIView_Tag]; - rssi_view.showText = @NO; - rssi_view.borderRadius = @5; - rssi_view.type = LDProgressSolid; - [cell addSubview:rssi_view]; + CGRect chartRect = CGRectMake(cellBounds.size.width - 100, 10, 30, cellBounds.size.height - 20); + + chartView = [MPPlot plotWithType : MPPlotTypeBars frame : chartRect]; + chartView.valueRanges = MPMakeGraphValuesRange(0, 100); + chartView.values = [[NSArray alloc] initWithObjects:@0, @0, @0, @0, @0, nil]; + chartView.graphColor = [UIColor colorWithRed:0.120 green:0.806 blue:0.157 alpha:1.000]; + + [chartView setTag:indexPath.row + kRSSIView_Tag]; - viewForCell = rssi_view; + [cell addSubview : chartView]; } + // Set signal strength as percent. // Assuming RSSI Value range -100 ~ 0 - float percentage = (p.RSSI + 100) / 100.f; - viewForCell.progress = percentage; + CGFloat percentage = (p.RSSI + 100); + + NSInteger step = Round(percentage / 20.0); // 20.f = Tick + NSMutableArray* valueArray = [[NSMutableArray alloc] init]; + for(NSInteger i = 0; i < 5; i ++) { + + if( i < step ) + [valueArray addObject:@((i+1) * 20)]; + else + [valueArray addObject:@0]; + } + + chartView.values = [NSArray arrayWithArray:valueArray]; [cell setPeripheral:p]; return cell; diff --git a/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.h b/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.h new file mode 100644 index 0000000..ff07f61 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.h @@ -0,0 +1,21 @@ +// +// MPBarsGraphView.h +// MPPlot +// +// Created by Alex Manzella on 22/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "MPPlot.h" + +@interface MPBarsGraphView : MPPlot{ + + BOOL shouldAnimate; + +} + +@property (nonatomic, readwrite) CGFloat topCornerRadius; + +@property (nonatomic, readwrite) NSArray* backValues; // background gray values - always be 0.2, 0.4, 0.6, 0.8, 1.0 + +@end diff --git a/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.m b/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.m new file mode 100644 index 0000000..dd19329 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/MPBarsGraphView.m @@ -0,0 +1,146 @@ +// +// MPBarsGraphView.m +// MPPlot +// +// Created by Alex Manzella on 22/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "MPBarsGraphView.h" + +@implementation MPBarsGraphView + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + + if (self) { + self.backgroundColor = [UIColor clearColor]; + currentTag = -1; + self.topCornerRadius = -1; + + // Initialize backValues + self.backValues = [[NSArray alloc] initWithObjects:@0.2, @0.4, @0.6, @0.8, @1.0, nil]; + } + + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + self.backgroundColor = [UIColor clearColor]; + currentTag = -1; + self.topCornerRadius = -1; + + // Initialize backValues + self.backValues = [[NSArray alloc] initWithObjects:@0.2, @0.4, @0.6, @0.8, @1.0, nil]; + } + + return self; +} + +- (void)drawRect:(CGRect)rect +{ + [super drawRect:rect]; + + if (self.values.count && !self.waitToUpdate) { + + for (UIView *subview in self.subviews) { + [subview removeFromSuperview]; + } + + [self addBarsAnimated:shouldAnimate]; + [self.graphColor setStroke]; + + UIBezierPath *line = [UIBezierPath bezierPath]; + [line moveToPoint:CGPointMake(PADDING, self.height)]; + [line addLineToPoint:CGPointMake(self.width-PADDING, self.height)]; + [line setLineWidth:1]; + [line stroke]; + } +} + +- (void)addBarsAnimated:(BOOL)animated +{ + for (UIButton* button in buttons) { + [button removeFromSuperview]; + } + + buttons=[[NSMutableArray alloc] init]; + + if (animated) { + self.layer.masksToBounds=YES; + } + + CGFloat barWidth = self.width/(points.count*2+1); + CGFloat radius = barWidth*(self.topCornerRadius >=0 ? self.topCornerRadius : 0.3); + + for (NSInteger i=0; i0.0 ? _animationDuration : .25; +} + +- (void)animate +{ + self.waitToUpdate=NO; + shouldAnimate=YES; + [self setNeedsDisplay]; +} + +@end diff --git a/Mooshimeter/Vendor/MPPlot/MPGraphView.h b/Mooshimeter/Vendor/MPPlot/MPGraphView.h new file mode 100644 index 0000000..59d6fa7 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/MPGraphView.h @@ -0,0 +1,20 @@ +// +// MPGraphView.h +// +// +// Created by Alex Manzella on 18/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import +#import "MPPlot.h" + +@interface MPGraphView : MPPlot{ + + CAGradientLayer *gradient; +} + +@property (nonatomic, assign) BOOL curved; +@property (nonatomic, retain) NSArray *fillColors; // array of colors or CGColor + +@end diff --git a/Mooshimeter/Vendor/MPPlot/MPGraphView.m b/Mooshimeter/Vendor/MPPlot/MPGraphView.m new file mode 100644 index 0000000..b76efaf --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/MPGraphView.m @@ -0,0 +1,225 @@ +// +// MPGraphView.m +// +// +// Created by Alex Manzella on 18/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "MPGraphView.h" +#import "UIBezierPath+curved.h" + +@implementation MPGraphView + ++ (Class)layerClass +{ + return [CAShapeLayer class]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + + if (self) { + self.backgroundColor = [UIColor clearColor]; + currentTag = -1; + } + + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + self.backgroundColor = [UIColor clearColor]; + currentTag = -1; + } + + return self; +} + +- (void)drawRect:(CGRect)rect +{ + [super drawRect:rect]; + + if (self.values.count && !self.waitToUpdate) { + ((CAShapeLayer *)self.layer).fillColor = [UIColor clearColor].CGColor; + ((CAShapeLayer *)self.layer).strokeColor = self.graphColor.CGColor; + ((CAShapeLayer *)self.layer).path = [self graphPathFromPoints].CGPath; + ((CAShapeLayer *)self.layer).lineWidth = self.lineWidth ? self.lineWidth : 1; + } +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *view = [super hitTest:point withEvent:event]; + + if (view==self) { + return nil; + } + + return view; +} + +- (UIBezierPath *)graphPathFromPoints +{ + BOOL fill=self.fillColors.count; + UIBezierPath *path=[UIBezierPath bezierPath]; + + for (UIButton* button in buttons) { + [button removeFromSuperview]; + } + + buttons=[[NSMutableArray alloc] init]; + + for (NSInteger i=0;i +#import "UIView+Frame.h" +#import "_MPWButton.h" + +#define PADDING 12 + +#define ANIMATIONDURATION 1.5 + +typedef NS_ENUM(NSUInteger, MPPlotType) { + MPPlotTypeUnknown=0, + MPPlotTypeGraph=1, + MPPlotTypeBars=2, + MPPlotTypeCake=3, +}; + +typedef CGFloat(^GraphPointsAlgorithm)(CGFloat x); + +struct _MPValuesRange { + CGFloat max; + CGFloat min; +}; + +typedef struct _MPValuesRange MPGraphValuesRange; + +NS_INLINE MPGraphValuesRange MPMakeGraphValuesRange(CGFloat min, CGFloat max) { + MPGraphValuesRange r; + r.min = min; + r.max = max; + return r; +} + +NS_INLINE BOOL MPValuesRangeNULL(MPGraphValuesRange r) { + return r.min==CGFLOAT_MIN && r.max==CGFLOAT_MAX; +} + +NS_INLINE MPGraphValuesRange MPGetBiggestRange(MPGraphValuesRange r1,MPGraphValuesRange r2) { + MPGraphValuesRange r; + r.min=(r1.minr2.max ? r1.max : r2.max); + return r; +} + +@protocol MPDetailView + +- (void)setText:(NSString *)text; + +@end + +@interface MPPlot : UIView{ + + MPPlotType plotType; + UIColor *_graphColor; + CGFloat _animationDuration; + NSArray *points; + NSMutableArray *buttons; + NSInteger currentTag; + GraphPointsAlgorithm _customAlgorithm; + NSUInteger _numberOfPoints; + +} + +// Abstract Class ++ (id)plotWithType:(MPPlotType)type frame:(CGRect)frame; ++ (MPGraphValuesRange)rangeForValues:(NSArray *)values; + +@property (nonatomic, copy) NSArray *values; // array of NSNumber or NSString +@property (nonatomic, retain) UIColor *graphColor; // color of the line +@property (nonatomic, assign) CGFloat lineWidth; +@property (nonatomic, assign) BOOL waitToUpdate; +@property (nonatomic, assign) CGFloat animationDuration; +@property (nonatomic, assign) MPGraphValuesRange valueRanges; // specify or read the max min... useful if you want to make 2 graph based on the same values, otherwise they would be inconsistent between them, for example graph of download has max 20 min 10 , and graph of updates has max 5 and min 0, without that the 20 and the 5 would be in the same place +// detail View customization +@property (nonatomic, readwrite) UIView *detailView; +@property (nonatomic, retain) UIColor *detailBackgroundColor; +@property (nonatomic, retain) UIColor *detailTextColor; +@property (nonatomic, retain) NSNumberFormatter *detailLabelFormatter; + +- (void)animate; +- (void)setAlgorithm:(GraphPointsAlgorithm)customAlgorithm numberOfPoints:(NSUInteger)numberOfPoints; +- (void)tap:(UIButton *)button; + +@end diff --git a/Mooshimeter/Vendor/MPPlot/MPPlot.m b/Mooshimeter/Vendor/MPPlot/MPPlot.m new file mode 100644 index 0000000..c0ce809 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/MPPlot.m @@ -0,0 +1,300 @@ +// +// MPPlot.m +// MPPlot +// +// Created by Alex Manzella on 22/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "MPPlot.h" +#import "MPGraphView.h" +#import "MPBarsGraphView.h" + +@implementation MPPlot + ++ (MPGraphValuesRange)rangeForValues:(NSArray *)values +{ + CGFloat min,max; + + min = max = [[values firstObject] floatValue]; + + for (NSInteger i=0; imax) { + max = val; + } + + if (val0.0 ? _animationDuration : ANIMATIONDURATION; +} + +#pragma mark Internal + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + // Marked by Jianying Shi. + // No need to display detail view. + // 05/03/2015 + +#if 0 + UIView *view = [super hitTest:point withEvent:event]; + + if (view==self && self.detailView.superview) { + [UIView animateWithDuration:.15 animations:^{ + self.detailView.transform=CGAffineTransformMakeScale(0, 0); + }completion:^(BOOL finished) { + [self.detailView removeFromSuperview]; + currentTag=-1; + }]; + } + + return view; +#else + + return nil; +#endif +} + +- (void)animate +{ + self.waitToUpdate=NO; + [self setNeedsDisplay]; +} + +#pragma mark Actions + +- (void)tap:(UIButton *)button +{ + if (button.tag==currentTag) { + currentTag = -1; + }else{ + currentTag = button.tag; + } + + if (self.detailView.superview) { + + [UIView animateWithDuration:.15 animations:^{ + self.detailView.transform = CGAffineTransformMakeScale(0, 0); + }completion:^(BOOL finished) { + [self.detailView removeFromSuperview]; + if(currentTag>=0) { + [self displayDetailViewAtPoint:button.center]; + } + }]; + + }else{ + [self displayDetailViewAtPoint:button.center]; + } +} + +- (void)displayDetailViewAtPoint:(CGPoint)point +{ + if ([[self.values objectAtIndex:currentTag] isKindOfClass:[NSString class]]) { + self.detailView.text = [self.values objectAtIndex:currentTag]; + }else{ + self.detailView.text = [NSString stringWithFormat:@"%@",[self.detailLabelFormatter stringFromNumber:[self.values objectAtIndex:currentTag]]]; + } + + self.detailView.center = point; + self.detailView.transform = CGAffineTransformMakeScale(0, 0); + [self addSubview:self.detailView]; + + [UIView animateWithDuration:.2 animations:^{ + self.detailView.transform = CGAffineTransformMakeScale(1, 1); + }]; +} + +-(UIView *)detailView +{ + if (_detailView) { + return _detailView; + } + + UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 80, 20)]; + label.textAlignment = NSTextAlignmentCenter; + label.textColor = self.detailTextColor; + label.backgroundColor = self.detailBackgroundColor; + label.layer.borderColor = label.textColor.CGColor; + label.layer.borderWidth = .5; + label.layer.cornerRadius = 3; + label.adjustsFontSizeToFitWidth = YES; + label.clipsToBounds = YES; + + self.detailView = (UILabel *)label; + + return _detailView; +} + +@end diff --git a/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.h b/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.h new file mode 100644 index 0000000..41ddf81 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.h @@ -0,0 +1,15 @@ +// +// UIBezierPath+curved.h +// MPPlot +// +// Created by Alex Manzella on 22/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import + +@interface UIBezierPath (curved) + +- (UIBezierPath*)smoothedPathWithGranularity:(NSInteger)granularity; + +@end diff --git a/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.m b/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.m new file mode 100644 index 0000000..dcb7321 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/UIBezierPath+curved.m @@ -0,0 +1,91 @@ +// +// UIBezierPath+curved.m +// MPPlot +// +// Created by Alex Manzella on 22/05/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "UIBezierPath+curved.h" + +// Based on code from Erica Sadun +void getPointsFromBezier(void *info, const CGPathElement *element); +NSArray *pointsFromBezierPath(UIBezierPath *bpath); + +#define VALUE(_INDEX_) [NSValue valueWithCGPoint:points[_INDEX_]] +#define POINT(_INDEX_) [(NSValue *)[points objectAtIndex:_INDEX_] CGPointValue] + +@implementation UIBezierPath (curved) + +// Get points from Bezier Curve +void getPointsFromBezier(void *info, const CGPathElement *element) +{ + NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info; + // Retrieve the path element type and its points + CGPathElementType type = element->type; + CGPoint *points = element->points; + + // Add the points if they're available (per type) + if (type != kCGPathElementCloseSubpath) + { + [bezierPoints addObject:VALUE(0)]; + if ((type != kCGPathElementAddLineToPoint) && + (type != kCGPathElementMoveToPoint)) + [bezierPoints addObject:VALUE(1)]; + } + if (type == kCGPathElementAddCurveToPoint) + [bezierPoints addObject:VALUE(2)]; +} + +NSArray *pointsFromBezierPath(UIBezierPath *bpath) +{ + NSMutableArray *points = [NSMutableArray array]; + CGPathApply(bpath.CGPath, (__bridge void *)points, getPointsFromBezier); + return points; +} + +- (UIBezierPath*)smoothedPathWithGranularity:(NSInteger)granularity; +{ + NSMutableArray *points = [pointsFromBezierPath(self) mutableCopy]; + + if (points.count < 4) return [self copy]; + + // Add control points to make the math make sense + [points insertObject:[points objectAtIndex:0] atIndex:0]; + [points addObject:[points lastObject]]; + + UIBezierPath *smoothedPath = [self copy]; + [smoothedPath removeAllPoints]; + [smoothedPath moveToPoint:POINT(0)]; + + for (NSUInteger index = 1; index < points.count - 2; index++) + { + CGPoint p0 = POINT(index - 1); + CGPoint p1 = POINT(index); + CGPoint p2 = POINT(index + 1); + CGPoint p3 = POINT(index + 2); + + // now add n points starting at p1 + dx/dy up until p2 using Catmull-Rom splines + for (int i = 1; i < granularity; i++) + { + float t = (float) i * (1.0f / (float) granularity); + float tt = t * t; + float ttt = tt * t; + + CGPoint pi; // intermediate point + pi.x = 0.5 * (2*p1.x+(p2.x-p0.x)*t + (2*p0.x-5*p1.x+4*p2.x-p3.x)*tt + (3*p1.x-p0.x-3*p2.x+p3.x)*ttt); + pi.y = 0.5 * (2*p1.y+(p2.y-p0.y)*t + (2*p0.y-5*p1.y+4*p2.y-p3.y)*tt + (3*p1.y-p0.y-3*p2.y+p3.y)*ttt); + [smoothedPath addLineToPoint:pi]; + } + + // Now add p2 + [smoothedPath addLineToPoint:p2]; + } + + // finish by adding the last point + [smoothedPath addLineToPoint:POINT(points.count - 1)]; + + return smoothedPath; +} + +@end diff --git a/Mooshimeter/Vendor/MPPlot/UIView+Frame.h b/Mooshimeter/Vendor/MPPlot/UIView+Frame.h new file mode 100644 index 0000000..0b77c91 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/UIView+Frame.h @@ -0,0 +1,15 @@ +// +// UIView+Frame.h +// GoodBarberV2 +// +// Created by MPow on 31/10/13. +// Copyright (c) 2013 DuoApps. All rights reserved. +// + +#import + +@interface UIView (Frame) + +@property(nonatomic, readwrite) CGFloat x,y,width,height; + +@end diff --git a/Mooshimeter/Vendor/MPPlot/UIView+Frame.m b/Mooshimeter/Vendor/MPPlot/UIView+Frame.m new file mode 100644 index 0000000..1bf3a4c --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/UIView+Frame.m @@ -0,0 +1,61 @@ +// +// UIView+Frame.m +// GoodBarberV2 +// +// Created by MPow on 31/10/13. +// Copyright (c) 2013 DuoApps. All rights reserved. +// + +#import "UIView+Frame.h" + +@implementation UIView (Frame) + +- (void)setX:(CGFloat)x +{ + CGRect frame=self.frame; + frame.origin.x=x; + self.frame=frame; +} + +- (void)setY:(CGFloat)y +{ + CGRect frame=self.frame; + frame.origin.y=y; + self.frame=frame; +} + +- (void)setWidth:(CGFloat)width +{ + CGRect frame=self.frame; + frame.size.width=width; + self.frame=frame; +} + +- (void)setHeight:(CGFloat)height +{ + CGRect frame=self.frame; + frame.size.height=height; + self.frame=frame; +} + +- (CGFloat)x +{ + return self.frame.origin.x; +} + +- (CGFloat)y +{ + return self.frame.origin.y; +} + +- (CGFloat)width +{ + return self.bounds.size.width; +} + +- (CGFloat)height +{ + return self.bounds.size.height; +} + +@end diff --git a/Mooshimeter/Vendor/MPPlot/_MPWButton.h b/Mooshimeter/Vendor/MPPlot/_MPWButton.h new file mode 100644 index 0000000..d067655 --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/_MPWButton.h @@ -0,0 +1,15 @@ +// +// _MPWButton.h +// MPPlotExample +// +// Created by Alex Manzella on 23/10/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import + +@interface _MPWButton : UIButton + +@property (nonatomic, assign) UIOffset tappableAreaOffset; + +@end diff --git a/Mooshimeter/Vendor/MPPlot/_MPWButton.m b/Mooshimeter/Vendor/MPPlot/_MPWButton.m new file mode 100644 index 0000000..5272a7e --- /dev/null +++ b/Mooshimeter/Vendor/MPPlot/_MPWButton.m @@ -0,0 +1,19 @@ +// +// _MPWButton.m +// MPPlotExample +// +// Created by Alex Manzella on 23/10/14. +// Copyright (c) 2014 mpow. All rights reserved. +// + +#import "_MPWButton.h" + +@implementation _MPWButton + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + return CGRectContainsPoint(CGRectInset(self.bounds, -self.tappableAreaOffset.horizontal, -self.tappableAreaOffset.vertical), point); +} + +@end + diff --git a/Mooshimeter/Vendor/PDChart/PDBarChart/PDBarChart.swift b/Mooshimeter/Vendor/PDChart/PDBarChart/PDBarChart.swift new file mode 100644 index 0000000..c0cf2fc --- /dev/null +++ b/Mooshimeter/Vendor/PDChart/PDBarChart/PDBarChart.swift @@ -0,0 +1,229 @@ +// +// PDBarChart.swift +// PDChart +// +// Created by Pandara on 14-7-11. +// Copyright (c) 2014年 Pandara. All rights reserved. +// + +import UIKit +import QuartzCore + +class PDBarChartDataItem { + //optional + var axesColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0) //坐标轴颜色 + var axesTipColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0) //坐标轴刻度值颜色 + var chartLayerColor: UIColor = UIColor(red: 61.0 / 255, green: 189.0 / 255, blue: 100.0 / 255, alpha: 1.0) //折线的颜色 + + var barBgColor: UIColor = UIColor(red: 234.0 / 255, green: 234.0 / 255, blue: 234.0 / 255, alpha: 1.0) + var barColor: UIColor = UIColor(red: 69.0 / 255, green: 189.0 / 255, blue: 100.0 / 255, alpha: 1.0)//69 189 100 + + var showAxes: Bool = true + var axesWidth: CGFloat = 1.0 + + var barMargin: CGFloat = 4.0 + var barWidth: CGFloat! + + var barCornerRadius: CGFloat = 0 + + //require + var xMax: CGFloat! + var xInterval: CGFloat! + + var yMax: CGFloat! + var yInterval: CGFloat! + + var xAxesDegreeTexts: [String]? + var yAxesDegreeTexts: [String]? + + var barPointArray: [CGPoint]? + + init() { + + } +} + +class PDBarChart: PDChart { + + var axesComponent: PDChartAxesComponent! + var dataItem: PDBarChartDataItem! + + //property + var barBgArray: [UIView] = [] + var barLayerArray: [CAShapeLayer] = [] + + init(frame: CGRect, dataItem: PDBarChartDataItem) { + super.init(frame: frame) + self.dataItem = dataItem + + var axesDataItem: PDChartAxesComponentDataItem = PDChartAxesComponentDataItem() + axesDataItem.arrowBodyLength += 10 + axesDataItem.targetView = self + axesDataItem.featureH = getFeatureHeight() + axesDataItem.featureW = getFeatureWidth() + axesDataItem.xMax = dataItem.xMax + axesDataItem.xInterval = dataItem.xInterval + axesDataItem.yMax = dataItem.yMax + axesDataItem.yInterval = dataItem.yInterval + axesDataItem.xAxesDegreeTexts = dataItem.xAxesDegreeTexts + axesDataItem.showXDegree = false + axesDataItem.axesWidth = dataItem.axesWidth + + self.axesComponent = PDChartAxesComponent(dataItem: axesDataItem) + + //bar width + var xDegreeInterval = self.axesComponent.getXDegreeInterval() + self.dataItem.barWidth = xDegreeInterval - dataItem.barMargin * 2 + + self.addBarBackgroundView() + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func getFeatureWidth() -> CGFloat { + return CGFloat(self.frame.size.width) + } + + func getFeatureHeight() -> CGFloat { + return CGFloat(self.frame.size.height) + } + + func getBarView(frame: CGRect) -> UIView { + var barView: UIView = UIView(frame: frame) + barView.backgroundColor = self.dataItem.barBgColor + barView.clipsToBounds = true + barView.layer.cornerRadius = self.dataItem.barCornerRadius + return barView + } + + func getBarShapeLayer(layerFrame: CGRect, barHeight: CGFloat) -> CAShapeLayer { + var shapeLayer: CAShapeLayer = CAShapeLayer() + shapeLayer.fillColor = UIColor.whiteColor().CGColor + shapeLayer.strokeColor = self.dataItem.barColor.CGColor + shapeLayer.lineWidth = self.dataItem.barWidth + shapeLayer.strokeStart = 0.0 + shapeLayer.strokeEnd = 1.0 + shapeLayer.frame = layerFrame + + var barPath: UIBezierPath = UIBezierPath() + barPath.moveToPoint(CGPointMake(self.dataItem.barWidth / 2, layerFrame.size.height)) + barPath.addLineToPoint(CGPointMake(self.dataItem.barWidth / 2, layerFrame.size.height - barHeight)) + barPath.stroke() + + shapeLayer.path = barPath.CGPath + + return shapeLayer + } + + func getBarAnimation() -> CABasicAnimation { + CATransaction.begin() + + var pathAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") + pathAnimation.duration = 1.0 + pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + pathAnimation.fromValue = 0.0 + pathAnimation.toValue = 1.0 + + CATransaction.setCompletionBlock({ + () -> Void in + return + }) + + CATransaction.commit() + + return pathAnimation + } + + func addBarBackgroundView() { + var xDegreeInterval: CGFloat = self.axesComponent.getXDegreeInterval() + var basePoint: CGPoint = self.axesComponent.getBasePoint() + var xAxesWidth: CGFloat = self.axesComponent.getXAxesWidth() + var yAxesHeight: CGFloat = self.axesComponent.getYAxesHeight() + + for var i = 0; i < self.dataItem.barPointArray!.count; i++ { + var point: CGPoint = self.dataItem.barPointArray![i] + + var bvw: CGFloat = self.dataItem.barWidth + var bvh: CGFloat = yAxesHeight + var bvx: CGFloat = basePoint.x + xDegreeInterval / 2 + self.dataItem.barMargin + (self.dataItem.barWidth + self.dataItem.barMargin * 2) * CGFloat(i) + var bvy: CGFloat = basePoint.y - bvh - self.dataItem.axesWidth / 2 + + var barView: UIView = self.getBarView(CGRectMake(bvx, bvy, bvw, bvh)) + self.barBgArray.append(barView) + self.addSubview(barView) + } + } + + override func strokeChart() { + if !(self.dataItem.barPointArray != nil) { + return + } + + UIGraphicsBeginImageContext(self.frame.size) + + var yAxesHeight: CGFloat = self.axesComponent.getYAxesHeight() + + for var i = 0; i < self.dataItem.barPointArray!.count; i++ { + var point: CGPoint = self.dataItem.barPointArray![i] + var barView: UIView = self.barBgArray[i] + + //barShape + var barShapeLayer: CAShapeLayer = self.getBarShapeLayer(CGRectMake(0, 0, barView.frame.size.width, barView.frame.size.height), barHeight: point.y / self.dataItem.yMax * yAxesHeight) + barShapeLayer.addAnimation(self.getBarAnimation(), forKey: "barAnimation") + self.barLayerArray.append(barShapeLayer) + barView.layer.addSublayer(barShapeLayer) + } + + UIGraphicsEndImageContext() + } + + override func drawRect(rect: CGRect) + { + super.drawRect(rect) + + var context: CGContextRef = UIGraphicsGetCurrentContext() + axesComponent.strokeAxes(context) + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/Vendor/PDChart/PDChart.swift b/Mooshimeter/Vendor/PDChart/PDChart.swift new file mode 100644 index 0000000..31521e3 --- /dev/null +++ b/Mooshimeter/Vendor/PDChart/PDChart.swift @@ -0,0 +1,36 @@ +// +// PDChart.swift +// Swift_iPhone_demo +// +// Created by Pandara on 14-7-2. +// Copyright (c) 2014年 Pandara. All rights reserved. +// + +import UIKit + +let yMargin: Double = 10 +let xMargin: Double = 10 + +class PDChart: UIView { + + + + override init(frame: CGRect) { + super.init(frame: frame) + self.backgroundColor = UIColor.whiteColor() + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func strokeChart() { + + } + + override func drawRect(rect: CGRect) + { + // Drawing code + + } +} diff --git a/Mooshimeter/Vendor/PDChart/PDChartComponent/PDChartAxesComponent.swift b/Mooshimeter/Vendor/PDChart/PDChartComponent/PDChartAxesComponent.swift new file mode 100644 index 0000000..ac13145 --- /dev/null +++ b/Mooshimeter/Vendor/PDChart/PDChartComponent/PDChartAxesComponent.swift @@ -0,0 +1,254 @@ +// +// PDChartAxesComponent.swift +// PDChart +// +// Created by Pandara on 14-7-11. +// Copyright (c) 2014年 Pandara. All rights reserved. +// + +import UIKit + +class PDChartAxesComponentDataItem: NSObject { + //required + var targetView: UIView! + + var featureH: CGFloat! + var featureW: CGFloat! + + var xMax: CGFloat! + var xInterval: CGFloat! + var yMax: CGFloat! + var yInterval: CGFloat! + + var xAxesDegreeTexts: [String]? + var yAxesDegreeTexts: [String]? + + //optional default + var showAxes: Bool = true + + var showXDegree: Bool = true + var showYDegree: Bool = true + + var axesColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0)//坐标轴颜色 + var axesTipColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0)//坐标轴刻度值颜色 + + var xAxesLeftMargin: CGFloat = 40 //坐标系左边margin + var xAxesRightMargin: CGFloat = 40 //坐标系右边margin + var yAxesBottomMargin: CGFloat = 40 //坐标系下面margin + var yAxesTopMargin: CGFloat = 40 //坐标系上方marign + + var axesWidth: CGFloat = 1.0 //坐标轴的粗细 + + var arrowHeight: CGFloat = 5.0 + var arrowWidth: CGFloat = 5.0 + var arrowBodyLength: CGFloat = 10.0 + + var degreeLength: CGFloat = 5.0 //坐标轴刻度直线的长度 + var degreeTipFontSize: CGFloat = 10.0 + var degreeTipMarginHorizon: CGFloat = 5.0 + var degreeTipMarginVertical: CGFloat = 5.0 + + override init() { + + } +} + +class PDChartAxesComponent: NSObject { + + var dataItem: PDChartAxesComponentDataItem! + + init(dataItem: PDChartAxesComponentDataItem) { + self.dataItem = dataItem + } + + func getYAxesHeight() -> CGFloat {//heigth between 0~yMax + var basePoint: CGPoint = self.getBasePoint() + var yAxesHeight = basePoint.y - dataItem.arrowHeight - dataItem.yAxesTopMargin - dataItem.arrowBodyLength + return yAxesHeight + } + + func getXAxesWidth() -> CGFloat {//width between 0~xMax + var basePoint: CGPoint = self.getBasePoint() + var xAxesWidth = dataItem.featureW - basePoint.x - dataItem.arrowHeight - dataItem.xAxesRightMargin - dataItem.arrowBodyLength + return xAxesWidth + } + + func getBasePoint() -> CGPoint { + + var neededAxesWidth: CGFloat! + if dataItem.showAxes { + neededAxesWidth = CGFloat(dataItem.axesWidth) + } else { + neededAxesWidth = 0 + } + + var basePoint: CGPoint = CGPoint(x: dataItem.xAxesLeftMargin + neededAxesWidth / 2.0, y: dataItem.featureH - (dataItem.yAxesBottomMargin + neededAxesWidth / 2.0)) + return basePoint + } + + func getXDegreeInterval() -> CGFloat { + var xAxesWidth: CGFloat = self.getXAxesWidth() + var xDegreeInterval: CGFloat = dataItem.xInterval / dataItem.xMax * xAxesWidth + return xDegreeInterval + } + + func getYDegreeInterval() -> CGFloat { + var yAxesHeight: CGFloat = self.getYAxesHeight() + var yDegreeInterval: CGFloat = dataItem.yInterval / dataItem.yMax * yAxesHeight + return yDegreeInterval + } + + func getAxesDegreeTipLabel(tipText: String, center: CGPoint, size: CGSize, fontSize: CGFloat, textAlignment: NSTextAlignment, textColor: UIColor) -> UILabel { + var label: UILabel = UILabel(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: size)) + label.text = tipText + label.center = center + label.textAlignment = textAlignment + label.textColor = textColor + label.backgroundColor = UIColor.clearColor() + label.adjustsFontSizeToFitWidth = true + label.font = UIFont.systemFontOfSize(fontSize) + return label + } + + func getXAxesDegreeTipLabel(tipText: String, center: CGPoint, size: CGSize, fontSize: CGFloat) -> UILabel { + return self.getAxesDegreeTipLabel(tipText, center: center, size: size, fontSize: fontSize, textAlignment: NSTextAlignment.Center, textColor: dataItem.axesTipColor) + } + + func getYAxesDegreeTipLabel(tipText: String, center: CGPoint, size: CGSize, fontSize: CGFloat) -> UILabel { + return self.getAxesDegreeTipLabel(tipText, center: center, size: size, fontSize: fontSize, textAlignment: NSTextAlignment.Right, textColor: dataItem.axesTipColor) + } + + func strokeAxes(context: CGContextRef?) { + var xAxesWidth: CGFloat = self.getXAxesWidth() + var yAxesHeight: CGFloat = self.getYAxesHeight() + var basePoint: CGPoint = self.getBasePoint() + + + if dataItem.showAxes { + CGContextSetStrokeColorWithColor(context, dataItem.axesColor.CGColor) + CGContextSetFillColorWithColor(context, dataItem.axesColor.CGColor) + + var axesPath: UIBezierPath = UIBezierPath() + axesPath.lineWidth = dataItem.axesWidth + axesPath.lineCapStyle = kCGLineCapRound + axesPath.lineJoinStyle = kCGLineJoinRound + + //x axes-------------------------------------- + axesPath.moveToPoint(CGPoint(x: basePoint.x, y: basePoint.y)) + axesPath.addLineToPoint(CGPoint(x: basePoint.x + xAxesWidth, y: basePoint.y)) + + //degrees in x axes + var xDegreeNum: Int = Int((dataItem.xMax - (dataItem.xMax % dataItem.xInterval)) / dataItem.xInterval) + var xDegreeInterval: CGFloat = self.getXDegreeInterval() + + if dataItem.showXDegree { + for var i = 0; i < xDegreeNum; i++ { + var degreeX: CGFloat = basePoint.x + xDegreeInterval * CGFloat(i + 1) + axesPath.moveToPoint(CGPoint(x: degreeX, y: basePoint.y)) + axesPath.addLineToPoint(CGPoint(x: degreeX, y: basePoint.y - dataItem.degreeLength)) + } + } + + //x axes arrow + //arrow body + axesPath.moveToPoint(CGPoint(x: basePoint.x + xAxesWidth, y: basePoint.y)) + axesPath.addLineToPoint(CGPoint(x: basePoint.x + xAxesWidth + dataItem.arrowBodyLength, y: basePoint.y)) + //arrow head + var arrowPath: UIBezierPath = UIBezierPath() + arrowPath.lineWidth = dataItem.axesWidth + arrowPath.lineCapStyle = kCGLineCapRound + arrowPath.lineJoinStyle = kCGLineJoinRound + + var xArrowTopPoint: CGPoint = CGPoint(x: basePoint.x + xAxesWidth + dataItem.arrowBodyLength + dataItem.arrowHeight, y: basePoint.y) + arrowPath.moveToPoint(xArrowTopPoint) + arrowPath.addLineToPoint(CGPoint(x: basePoint.x + xAxesWidth + dataItem.arrowBodyLength, y: basePoint.y - dataItem.arrowWidth / 2)) + arrowPath.addLineToPoint(CGPoint(x: basePoint.x + xAxesWidth + dataItem.arrowBodyLength, y: basePoint.y + dataItem.arrowWidth / 2)) + arrowPath.addLineToPoint(xArrowTopPoint) + + //y axes-------------------------------------- + axesPath.moveToPoint(CGPoint(x: basePoint.x, y: basePoint.y)) + axesPath.addLineToPoint(CGPoint(x: basePoint.x, y: basePoint.y - yAxesHeight)) + + //degrees in y axes + var yDegreesNum: Int = Int((dataItem.yMax - (dataItem.yMax % dataItem.yInterval)) / dataItem.yInterval) + var yDegreeInterval: CGFloat = self.getYDegreeInterval() + if dataItem.showYDegree { + for var i = 0; i < yDegreesNum; i++ { + var degreeY: CGFloat = basePoint.y - yDegreeInterval * CGFloat(i + 1) + axesPath.moveToPoint(CGPoint(x: basePoint.x, y: degreeY)) + axesPath.addLineToPoint(CGPoint(x: basePoint.x + dataItem.degreeLength, y: degreeY)) + } + } + + //y axes arrow + //arrow body + axesPath.moveToPoint(CGPoint(x: basePoint.x, y: basePoint.y - yAxesHeight)) + axesPath.addLineToPoint(CGPoint(x: basePoint.x, y: basePoint.y - yAxesHeight - dataItem.arrowBodyLength)) + //arrow head + var yArrowTopPoint: CGPoint = CGPoint(x: basePoint.x, y: basePoint.y - yAxesHeight - dataItem.arrowBodyLength - dataItem.arrowHeight) + arrowPath.moveToPoint(yArrowTopPoint) + arrowPath.addLineToPoint(CGPoint(x: basePoint.x - dataItem.arrowWidth / 2, y: basePoint.y - yAxesHeight - dataItem.arrowBodyLength)) + arrowPath.addLineToPoint(CGPoint(x: basePoint.x + dataItem.arrowWidth / 2, y: basePoint.y - yAxesHeight - dataItem.arrowBodyLength)) + arrowPath.addLineToPoint(yArrowTopPoint) + + axesPath.stroke() + arrowPath.stroke() + + //axes tips------------------------------------ + //func getXAxesDegreeTipLabel(tipText: String, frame: CGRect, fontSize: CGFloat) -> UILabel { + if (dataItem.xAxesDegreeTexts != nil) { + for var i = 0; i < dataItem.xAxesDegreeTexts!.count; i++ { + var size: CGSize = CGSize(width: xDegreeInterval - dataItem.degreeTipMarginHorizon * 2, height: dataItem.degreeTipFontSize) + var center: CGPoint = CGPoint(x: basePoint.x + xDegreeInterval * CGFloat(i + 1), y: basePoint.y + dataItem.degreeTipMarginVertical + size.height / 2) + var label: UILabel = self.getXAxesDegreeTipLabel(dataItem.xAxesDegreeTexts![i], center: center, size: size, fontSize: dataItem.degreeTipFontSize) + dataItem.targetView.addSubview(label) + } + } else { + for var i = 0; i < xDegreeNum; i++ { + var size: CGSize = CGSize(width: xDegreeInterval - dataItem.degreeTipMarginHorizon * 2, height: dataItem.degreeTipFontSize) + var center: CGPoint = CGPoint(x: basePoint.x + xDegreeInterval * CGFloat(i + 1), y: basePoint.y + dataItem.degreeTipMarginVertical + size.height / 2) + var label: UILabel = self.getXAxesDegreeTipLabel("\(CGFloat(i + 1) * dataItem.xInterval)", center: center, size: size, fontSize: dataItem.degreeTipFontSize) + dataItem.targetView.addSubview(label) + } + } + + if (dataItem.yAxesDegreeTexts != nil) { + for var i = 0; i < dataItem.yAxesDegreeTexts!.count; i++ { + var size: CGSize = CGSize(width: dataItem.xAxesLeftMargin - dataItem.degreeTipMarginHorizon * 2, height: dataItem.degreeTipFontSize) + var center: CGPoint = CGPoint(x: dataItem.xAxesLeftMargin / 2, y: basePoint.y - yDegreeInterval * CGFloat(i + 1)) + var label: UILabel = self.getYAxesDegreeTipLabel(dataItem.yAxesDegreeTexts![i], center: center, size: size, fontSize: dataItem.degreeTipFontSize) + dataItem.targetView.addSubview(label) + } + } else { + for var i = 0; i < yDegreesNum; i++ { + var size: CGSize = CGSize(width: dataItem.xAxesLeftMargin - dataItem.degreeTipMarginHorizon * 2, height: dataItem.degreeTipFontSize) + var center: CGPoint = CGPoint(x: dataItem.xAxesLeftMargin / 2, y: basePoint.y - yDegreeInterval * CGFloat(i + 1)) + var label: UILabel = self.getYAxesDegreeTipLabel("\(CGFloat(i + 1) * dataItem.yInterval)", center: center, size: size, fontSize: dataItem.degreeTipFontSize) + dataItem.targetView.addSubview(label) + } + } + } + + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/Vendor/PDChart/PDLineChart/PDLineChart.swift b/Mooshimeter/Vendor/PDChart/PDLineChart/PDLineChart.swift new file mode 100644 index 0000000..164b691 --- /dev/null +++ b/Mooshimeter/Vendor/PDChart/PDLineChart/PDLineChart.swift @@ -0,0 +1,158 @@ +// +// PDLineChart.swift +// Swift_iPhone_demo +// +// Created by Pandara on 14-7-3. +// Copyright (c) 2014年 Pandara. All rights reserved. +// + +import UIKit +import QuartzCore + +class PDLineChartDataItem { + //optional + var axesColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0) //坐标轴颜色 + var axesTipColor: UIColor = UIColor(red: 80.0 / 255, green: 80.0 / 255, blue: 80.0 / 255, alpha: 1.0) //坐标轴刻度值颜色 + var chartLayerColor: UIColor = UIColor(red: 61.0 / 255, green: 189.0 / 255, blue: 100.0 / 255, alpha: 1.0) //折线的颜色 + + var showAxes: Bool = true + + var xAxesDegreeTexts: [String]? + var yAxesDegreeTexts: [String]? + + //require + var xMax: CGFloat! + var xInterval: CGFloat! + + var yMax: CGFloat! + var yInterval: CGFloat! + + var pointArray: [CGPoint]?//按照数学中的平面二维坐标系输入数据 + + init() { + + } +} + +class PDLineChart: PDChart { + var axesComponent: PDChartAxesComponent! + var dataItem: PDLineChartDataItem! + + init(frame: CGRect, dataItem: PDLineChartDataItem) { + super.init(frame: frame) + + self.dataItem = dataItem + + var axesDataItem: PDChartAxesComponentDataItem = PDChartAxesComponentDataItem() + axesDataItem.targetView = self + axesDataItem.featureH = self.getFeatureHeight() + axesDataItem.featureW = self.getFeatureWidth() + axesDataItem.xMax = dataItem.xMax + axesDataItem.xInterval = dataItem.xInterval + axesDataItem.yMax = dataItem.yMax + axesDataItem.yInterval = dataItem.yInterval + axesDataItem.xAxesDegreeTexts = dataItem.xAxesDegreeTexts + axesDataItem.yAxesDegreeTexts = dataItem.yAxesDegreeTexts + + axesComponent = PDChartAxesComponent(dataItem: axesDataItem) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func getFeatureWidth() -> CGFloat { + return CGFloat(self.frame.size.width) + } + + func getFeatureHeight() -> CGFloat { + return CGFloat(self.frame.size.height) + } + + override func strokeChart() { + if !(self.dataItem.pointArray != nil) { + return + } + + //绘图layer + var chartLayer: CAShapeLayer = CAShapeLayer() + chartLayer.lineCap = kCALineCapRound + chartLayer.lineJoin = kCALineJoinRound + chartLayer.fillColor = UIColor.whiteColor().CGColor + chartLayer.strokeColor = self.dataItem.chartLayerColor.CGColor + chartLayer.lineWidth = 2.0 + chartLayer.strokeStart = 0.0 + chartLayer.strokeEnd = 1.0 + self.layer.addSublayer(chartLayer) + + //画线段 + UIGraphicsBeginImageContext(self.frame.size) + + var progressLine: UIBezierPath = UIBezierPath() + + var basePoint: CGPoint = axesComponent.getBasePoint() + var xAxesWidth: CGFloat = axesComponent.getXAxesWidth() + var yAxesHeight: CGFloat = axesComponent.getYAxesHeight() + for var i = 0; i < self.dataItem.pointArray!.count; i++ { + var point: CGPoint = self.dataItem.pointArray![i] + var pixelPoint: CGPoint = CGPoint(x: basePoint.x + point.x / self.dataItem.xMax * xAxesWidth, y: basePoint.y - point.y / self.dataItem.yMax * yAxesHeight)//转换为可以绘制的,屏幕中的像素点 + + if i == 0 { + progressLine.moveToPoint(pixelPoint) + } else { + progressLine.addLineToPoint(pixelPoint) + } + } + + progressLine.stroke() + + chartLayer.path = progressLine.CGPath + + //动画 + CATransaction.begin() + var pathAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") + pathAnimation.duration = 1.0 + pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + pathAnimation.fromValue = 0.0 + pathAnimation.toValue = 1.0 + + //func addAnimation(anim: CAAnimation!, forKey key: String!) + chartLayer.addAnimation(pathAnimation, forKey: "strokeEndAnimation") + + + //class func setCompletionBlock(block: (() -> Void)!) + CATransaction.setCompletionBlock({ + () -> Void in + }) + CATransaction.commit() + + UIGraphicsEndImageContext() + } + + override func drawRect(rect: CGRect) { + super.drawRect(rect) + + var context: CGContext = UIGraphicsGetCurrentContext() + axesComponent.strokeAxes(context) + } +} + + + + + + + + + + + + + + + + + + + + diff --git a/Mooshimeter/Vendor/PDChart/PDPieChart/PDPieChart.swift b/Mooshimeter/Vendor/PDChart/PDPieChart/PDPieChart.swift new file mode 100644 index 0000000..728651e --- /dev/null +++ b/Mooshimeter/Vendor/PDChart/PDPieChart/PDPieChart.swift @@ -0,0 +1,179 @@ +// +// PDPieChart.swift +// Swift_iPhone_demo +// +// Created by Pandara on 14-7-7. +// Copyright (c) 2014年 Pandara. All rights reserved. +// + +import UIKit +import QuartzCore + +struct PieDataItem { + var description: String? + var color: UIColor? + var percentage: CGFloat! +} + +let lightGreen: UIColor = UIColor(red: 69.0 / 255, green: 212.0 / 255, blue: 103.0 / 255, alpha: 1.0) +let middleGreen: UIColor = UIColor(red: 66.0 / 255, green: 187.0 / 255, blue: 102.0 / 255, alpha: 1.0) +let deepGreen: UIColor = UIColor(red: 64.0 / 255, green: 164.0 / 255, blue: 102.0 / 255, alpha: 1.0) + +class PDPieChartDataItem { + //optional + var animationDur: CGFloat = 1.0 + var clockWise: Bool = true + var chartStartAngle: CGFloat = CGFloat(-M_PI / 2) + var pieTipFontSize: CGFloat = 20.0 + var pieTipTextColor: UIColor = UIColor.whiteColor() + var pieMargin: CGFloat = 0 + + //required + var pieWidth: CGFloat! + var dataArray: [PieDataItem]! + + init() { + + } +} + +class PDPieChart: PDChart { + var dataItem: PDPieChartDataItem! + + init(frame: CGRect, dataItem: PDPieChartDataItem) { + super.init(frame: frame) + self.dataItem = dataItem + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func getShapeLayerWithARCPath(color: UIColor?, lineWidth: CGFloat, center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockWise: Bool) -> CAShapeLayer { + //layer + var pathLayer: CAShapeLayer = CAShapeLayer() + pathLayer.lineCap = kCALineCapButt + pathLayer.fillColor = UIColor.clearColor().CGColor + if (color != nil) { + pathLayer.strokeColor = color!.CGColor + } + pathLayer.lineWidth = self.dataItem.pieWidth + pathLayer.strokeStart = 0.0 + pathLayer.strokeEnd = 1.0 + pathLayer.backgroundColor = UIColor.clearColor().CGColor + + //path + var path: UIBezierPath = UIBezierPath() + path.lineWidth = lineWidth + path.addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: self.dataItem.clockWise) + path.stroke() + + pathLayer.path = path.CGPath + return pathLayer + } + + override func strokeChart() { + var pieCenterPointArray: [CGPoint] = [] + var radius: CGFloat = self.frame.size.width / 2 - self.dataItem.pieMargin - self.dataItem.pieWidth / 2 + var center: CGPoint = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2) + + var chartLayer: CAShapeLayer = CAShapeLayer() + chartLayer.backgroundColor = UIColor.clearColor().CGColor + self.layer.addSublayer(chartLayer) + + UIGraphicsBeginImageContext(self.frame.size) + //每一段饼 + var totalPercentage: CGFloat = 0.0 + for var i = 0; i < self.dataItem.dataArray.count; i++ { + //data + var dataItem: PieDataItem = self.dataItem.dataArray[i] + var startAngle: CGFloat = CGFloat(M_PI * 2.0) * totalPercentage + self.dataItem.chartStartAngle + var endAngle: CGFloat = startAngle + dataItem.percentage * CGFloat(M_PI * 2) + + //pie center point + var sign: Int = self.dataItem.clockWise ? -1 : 1 + var angle: Float = Float(dataItem.percentage) * Float(M_PI) * Float(2.0) / 2.0 + Float(totalPercentage) * Float(M_PI * 2.0) + + var pieCenterPointX: CGFloat = center.x + radius * CGFloat(sinf(angle)) + var pieCenterPointY: CGFloat = center.y - radius * CGFloat(cosf(angle)) + var pieCenterPoint: CGPoint = CGPoint(x: pieCenterPointX, y: pieCenterPointY) + pieCenterPointArray.append(pieCenterPoint) + + totalPercentage += dataItem.percentage + + //arc path layer + var pathLayer = self.getShapeLayerWithARCPath(dataItem.color, lineWidth: self.dataItem.pieWidth, center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockWise: true) + chartLayer.addSublayer(pathLayer) + } + + //mask + var maskCenter: CGPoint = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2) + var maskRadius: CGFloat = self.frame.size.width / 2 - self.dataItem.pieMargin - self.dataItem.pieWidth / 2 + var maskStartAngle: CGFloat = -CGFloat(M_PI) / 2 + var maskEndAngle: CGFloat = CGFloat(M_PI) * 2 - CGFloat(M_PI) / 2 + var maskLayer: CAShapeLayer = self.getShapeLayerWithARCPath(UIColor.whiteColor(), lineWidth: self.dataItem.pieWidth, center: maskCenter, radius: maskRadius, startAngle: maskStartAngle, endAngle: maskEndAngle, clockWise: true) + maskLayer.strokeStart = 0.0 + maskLayer.strokeEnd = 1.0 + + var animation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") + animation.duration = CFTimeInterval(self.dataItem.animationDur) + animation.fromValue = 0.0 + animation.toValue = 1.0 + animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + animation.removedOnCompletion = true + maskLayer.addAnimation(animation, forKey: "maskAnimation") + + self.layer.mask = maskLayer + + UIGraphicsEndImageContext() + + //pie tip + for var i = 0; i < pieCenterPointArray.count; i++ { + var dataItem: PieDataItem = self.dataItem.dataArray[i] + + var pieTipLabel: UILabel = UILabel() + pieTipLabel.backgroundColor = UIColor.clearColor(); + pieTipLabel.font = UIFont.systemFontOfSize(self.dataItem.pieTipFontSize) + pieTipLabel.textColor = self.dataItem.pieTipTextColor + if (dataItem.description != nil) { + pieTipLabel.text = dataItem.description + } else { + pieTipLabel.text = "\(dataItem.percentage * 100)%" + } + pieTipLabel.sizeToFit() + pieTipLabel.alpha = 0.0 + pieTipLabel.center = pieCenterPointArray[i] + + UIView.animateWithDuration(0.5, delay: NSTimeInterval(self.dataItem.animationDur), options: UIViewAnimationOptions.CurveEaseInOut, animations:{ + () -> Void in + pieTipLabel.alpha = 1.0 + }, completion: { + (completion: Bool) -> Void in + return + }) + + self.addSubview(pieTipLabel) + } + } + + override func drawRect(rect: CGRect) + { + super.drawRect(rect) + } + +} + + + + + + + + + + + + + + + From 6dea9a6dc103cf624a9979e9e4f5ab83be0057c8 Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Fri, 8 May 2015 10:09:21 +0800 Subject: [PATCH 5/9] fixed signal strength bar position, refresh button disappearing issue, connection timeout issue --- LGBluetooth/LGPeripheral.m | 6 +- Mooshimeter/AppDelegate.m | 16 ++++-- Mooshimeter/MeterViewController.h | 1 - Mooshimeter/MeterViewController.m | 4 ++ Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/MooshimeterDevice.m | 11 +++- Mooshimeter/ScanViewController.h | 1 + Mooshimeter/ScanViewController.m | 89 ++++++++++++++++++++---------- 8 files changed, 87 insertions(+), 43 deletions(-) diff --git a/LGBluetooth/LGPeripheral.m b/LGBluetooth/LGPeripheral.m index 8eacccd..f5949e1 100755 --- a/LGBluetooth/LGPeripheral.m +++ b/LGBluetooth/LGPeripheral.m @@ -107,9 +107,9 @@ - (void)connectWithTimeout:(NSUInteger)aWatchDogInterval completion:(LGPeripheralConnectionCallback)aCallback { [self connectWithCompletion:aCallback]; - [self performSelector:@selector(connectionWatchDogFired) - withObject:nil - afterDelay:aWatchDogInterval]; + [self performSelector : @selector(connectionWatchDogFired) + withObject : nil + afterDelay : aWatchDogInterval]; } - (void)disconnectWithCompletion:(LGPeripheralConnectionCallback)aCallback diff --git a/Mooshimeter/AppDelegate.m b/Mooshimeter/AppDelegate.m index 34f465a..d4f912b 100644 --- a/Mooshimeter/AppDelegate.m +++ b/Mooshimeter/AppDelegate.m @@ -68,7 +68,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( b.userInteractionEnabled = YES; [b addTarget:self action:@selector(settings_button_press) forControlEvents:UIControlEventTouchUpInside]; -#if 1 // Uncomment if needs classic button +#if 1 // Commented if needs classic button [b.titleLabel setFont:[UIFont systemFontOfSize:24]]; [b setTitle:@"\u2699" forState:UIControlStateNormal]; #else @@ -147,7 +147,7 @@ - (void)scanForMeters NSArray* services = [NSArray arrayWithObjects:[BLEUtility expandToMooshimUUID:METER_SERVICE_UUID], [BLEUtility expandToMooshimUUID:OAD_SERVICE_UUID], [CBUUID UUIDWithData:[NSData dataWithBytes:&tmp length:2]], nil]; NSLog(@"Refresh requested"); - [self.scan_vc.refreshControl beginRefreshing]; + // [self.scan_vc.refreshControl beginRefreshing]; NSTimer* refresh_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.scan_vc selector:@selector(reloadData) userInfo:nil repeats:YES]; @@ -202,7 +202,8 @@ -(void)handleScanViewSelect:(LGPeripheral*)p { [g_meter connect]; [self.scan_vc reloadData]; - break;} + break; + } } } @@ -220,6 +221,9 @@ -(void)switchToGraphView:(UIDeviceOrientation)new_o { } -(void)updateFirmwareIfNeeded { + if ( g_meter == nil ) + return; + if( g_meter->oad_mode ) { // We connected to a meter in OAD mode as requested previously. Update firmware. NSLog(@"Connected in OAD mode"); @@ -245,8 +249,6 @@ -(void)updateFirmwareIfNeeded UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Firmware Update" message:@"This meter requires a firmware update. This will take about a minute. Upgrade now?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Upgrade Now", nil]; [alert show]; } - - } #pragma mark ScatterViewControllerDelegate @@ -307,7 +309,9 @@ -(void)meterDisconnected { [self.bat_label setText:@""]; [self.rssi_label setText:@""]; [NSTimer cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateRSSI) object:nil]; - [self.nav popToViewController:self.scan_vc animated:YES]; + + // [self.nav popToViewController:self.scan_vc animated:YES]; + [self.scan_vc reloadData]; g_meter = nil; } diff --git a/Mooshimeter/MeterViewController.h b/Mooshimeter/MeterViewController.h index a7768ad..9ff076f 100644 --- a/Mooshimeter/MeterViewController.h +++ b/Mooshimeter/MeterViewController.h @@ -28,7 +28,6 @@ along with this program. If not, see . @protocol MeterViewControllerDelegate @required -(void)switchToGraphView:(UIDeviceOrientation)new_o; --(void)updateFirmwareIfNeeded; @end @interface MeterViewController : UIViewController diff --git a/Mooshimeter/MeterViewController.m b/Mooshimeter/MeterViewController.m index 6f4262f..29e51cc 100644 --- a/Mooshimeter/MeterViewController.m +++ b/Mooshimeter/MeterViewController.m @@ -86,6 +86,10 @@ - (void)viewDidLoad [v addSubview:sv]; [self.view addSubview:v]; + + // Trying to customized back button + UIBarButtonItem* back_item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToScanView:)]; + self.navigationItem.leftBarButtonItem = back_item; } - (void) backToScanView : (id) sender diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index 3d0ec1b..3502c12 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 520 + 573 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/MooshimeterDevice.m b/Mooshimeter/MooshimeterDevice.m index be7888e..1a201ec 100644 --- a/Mooshimeter/MooshimeterDevice.m +++ b/Mooshimeter/MooshimeterDevice.m @@ -77,11 +77,18 @@ -(void)populateLGDict:(NSArray*)characteristics { -(void)connect { self.chars = [[NSMutableDictionary alloc] init]; - [self.p connectWithTimeout:5 completion:^(NSError *error) { + [self.p connectWithTimeout : 30 completion:^(NSError *error) { NSLog(@"Discovering services"); + [self.p discoverServicesWithCompletion:^(NSArray *services, NSError *error) { for (LGService *service in services) { - if([service.UUIDString isEqualToString:[BLEUtility expandToMooshimUUIDString:METER_SERVICE_UUID]]) { + + NSString* meterServiceUUID = [BLEUtility expandToMooshimUUIDString:METER_SERVICE_UUID]; + + //NSLog(@"service UUID = %@", service.UUIDString); + //NSLog(@"meterSevice UUID = %@", service.UUIDString); + + if([service.UUIDString isEqualToString : meterServiceUUID]) { NSLog(@"METER SERVICE FOUND. Discovering characteristics."); self->oad_mode = NO; [service discoverCharacteristicsWithCompletion:^(NSArray *characteristics, NSError *error) { diff --git a/Mooshimeter/ScanViewController.h b/Mooshimeter/ScanViewController.h index 966ac35..f9d74c3 100644 --- a/Mooshimeter/ScanViewController.h +++ b/Mooshimeter/ScanViewController.h @@ -29,6 +29,7 @@ along with this program. If not, see . @required -(void)handleScanViewRefreshRequest; -(void)handleScanViewSelect:(LGPeripheral*)p; +-(void)updateFirmwareIfNeeded; @end @interface ScanViewController : UITableViewController diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index ea799f4..11de541 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -68,19 +68,24 @@ -(void)reloadData { [self.tableView reloadData]; // Auto - connect logics. + // Jianying Shi + +#if 0 NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; if( savedUUID != nil && savedUUID.length > 0 ) { for( NSInteger i = 0; i < self.peripherals.count; i ++ ) { LGPeripheral* p = self.peripherals[i]; - if( [[p UUIDString] isEqualToString:savedUUID] == YES ) + if( [[p UUIDString] isEqualToString : savedUUID] == YES ) { [self.delegate handleScanViewSelect:p]; break; } } } +#endif + } // by Jianying Shi. @@ -131,6 +136,7 @@ - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } +#pragma mark - Long-press menu - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer { // Check if menu is currently showing. @@ -155,21 +161,6 @@ - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; } - // Display check ID -#if 0 - NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; - NSString* deviceUUID = [g_meter.p UUIDString]; - - KxMenuItem* checkItem = settingsMenu.menuItems[2]; - if( savedUUID == nil || [savedUUID isEqualToString:deviceUUID] == NO ) - { - checkItem.image = nil; - } - else{ - checkItem.image = [UIImage imageNamed:@"checkicon"]; - } -#endif - // Check tapped table view cell CGPoint p = [gestureRecognizer locationInView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p]; @@ -177,19 +168,36 @@ - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer NSLog(@"long press on table view but not on a row"); } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { - // UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath]; - // CGRect buttonRect = cell.frame; - - // Adjust button rect origin - // buttonRect.origin = p; - // buttonRect.origin.y += buttonRect.size.height; - + // Display setting menu CGRect boundRect = CGRectMake(p.x, p.y, 5, 5); boundRect.origin.y += 64; - [settingsMenu setTag:kContextMenu_Tag]; + [settingsMenu setTag : indexPath.row + kContextMenu_Tag]; [settingsMenu showMenuInView:self.tableView fromRect:boundRect]; + + // Display auto-connect checked state + ScanTableViewCell* c = (ScanTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath]; + LGPeripheral* peripheral = c.p; + + NSMutableArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUIDs"]; + NSString* deviceUUID = [peripheral UUIDString]; + + KxMenuItem* checkItem = settingsMenu.menuItems[2]; + + if( savedUUIDs != nil && savedUUIDs.count > 0 ) + { + for(int i = 0; i < savedUUIDs.count; i ++) { + if( [[savedUUIDs objectAtIndex:i] isEqualToString:deviceUUID] ) + { + checkItem.image = [UIImage imageNamed:@"checkicon"]; + break; + } + } + } + else { + checkItem.image = nil; + } } else { NSLog(@"gestureRecognizer.state = %d", gestureRecognizer.state); } @@ -227,10 +235,22 @@ - (NSArray*) createSettingsMenuItems { - (void) firmwareUpdatePressed : (id) sender { +#if 0 // MARK + NSLog(@"Firmware Update"); -#if 0 - [self.delegate updateFirmwareIfNeeded]; + // By Jianying Shi. 05/02/2015 + // First of all, connect to peripheral + + // Get selected table view cell + NSInteger tag = settingsMenu.tag; + NSIndexPath* indexPath = [NSIndexPath indexPathForRow:tag inSection:0]; + + ScanTableViewCell* c = (ScanTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath]; + [self.delegate handleScanViewSelect:c.p]; + + // Then upldate + // [self.delegate updateFirmwareIfNeeded]; #endif } @@ -238,11 +258,20 @@ - (void) autoConnectPressed : (id) sender { NSLog(@"Auto connect"); -#if 0 NSString* deviceUUID = [g_meter.p UUIDString]; if( deviceUUID ) - [[NSUserDefaults standardUserDefaults] setObject:deviceUUID forKey:@"autoUUID"]; -#endif + { + // Save current device UUID to user defaults + NSMutableArray* savedUUIDs = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:@"autoUUIDs"]; + if( savedUUIDs != nil ) // already has saved id + [savedUUIDs addObject:deviceUUID]; + else { + savedUUIDs = [[NSMutableArray alloc] initWithObjects:deviceUUID, nil]; + } + + [[NSUserDefaults standardUserDefaults] setObject:savedUUIDs forKey:@"autoUUIDs"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } } @@ -309,7 +338,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if( chartView == nil ) { // Not yet created CGRect cellBounds = [cell frame]; - CGRect chartRect = CGRectMake(cellBounds.size.width - 100, 10, 30, cellBounds.size.height - 20); + CGRect chartRect = CGRectMake(cellBounds.size.width - 80, 10, 30, cellBounds.size.height - 20); chartView = [MPPlot plotWithType : MPPlotTypeBars frame : chartRect]; chartView.valueRanges = MPMakeGraphValuesRange(0, 100); From 19fbb97ae79dae0e915ef3cf21bf5e8f39bf21e7 Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Sat, 9 May 2015 13:12:39 +0800 Subject: [PATCH 6/9] connect app life cycle to disconnect / connect issue solved --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 418 +----------------- Mooshimeter/AppDelegate.h | 5 +- Mooshimeter/AppDelegate.m | 92 +++- Mooshimeter/GraphViewController.m | 28 ++ Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/ScanViewController.m | 131 +++--- 6 files changed, 194 insertions(+), 482 deletions(-) diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 19a528f..2a086e6 100644 --- a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -112,12 +112,12 @@ moduleName = "Mooshimeter" usesParentBreakpointCondition = "Yes" urlString = "file:///Users/admin/Desktop/Elance%20Job/Mooshimeter/LGBluetooth/LGUtils.m" - timestampString = "452314436.875006" + timestampString = "452840892.708755" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "181" endingLineNumber = "181" - offsetFromSymbolStart = "458"> + offsetFromSymbolStart = "386"> + offsetFromSymbolStart = "394"> @@ -151,146 +151,6 @@ endingLineNumber = "266"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mooshimeter/AppDelegate.h b/Mooshimeter/AppDelegate.h index aa85c99..4b346fc 100644 --- a/Mooshimeter/AppDelegate.h +++ b/Mooshimeter/AppDelegate.h @@ -52,7 +52,10 @@ along with this program. If not, see . @property (strong,nonatomic) UILabel* bat_label; @property (strong,nonatomic) UILabel* rssi_label; -@property (strong, nonatomic) UIButton* settings_button; +@property (strong,nonatomic) UIButton* settings_button; + +// By Jianying +@property (strong,nonatomic) NSString* mlastConnectedUDID; -(UINavigationController*)getNav; -(void)scanForMeters; diff --git a/Mooshimeter/AppDelegate.m b/Mooshimeter/AppDelegate.m index d4f912b..3a51565 100644 --- a/Mooshimeter/AppDelegate.m +++ b/Mooshimeter/AppDelegate.m @@ -90,6 +90,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.oad_profile.navCtrl = self.nav; [self.window setRootViewController:self.nav]; + + // By Jianying Shi. + // Install Key Window + [self.window makeKeyAndVisible]; + self.mlastConnectedUDID = nil; + return YES; } @@ -98,13 +104,22 @@ - (void)applicationWillResignActive:(UIApplication *)application // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - + // By Jianying Shi + // 05/08/2015 + if( g_meter ) // It means, current mooshim device is connected + { + // Save current peripheral UUID to dictionary + self.mlastConnectedUDID = g_meter.p.UUIDString; + + // Trying to disconnect. + [g_meter disconnect:nil]; + } } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits } - (void)applicationWillEnterForeground:(UIApplication *)application @@ -115,6 +130,17 @@ - (void)applicationWillEnterForeground:(UIApplication *)application - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + + // By Jianying Shi + // 05/08/2015 + if( self.mlastConnectedUDID != nil && self.mlastConnectedUDID.length > 0 ) // Previously background disconnect + { + // Refresh Device List && + // Display Please wait dialg + [SVProgressHUD showWithStatus:@"Restoring connection state, Please wait for a second."]; + + [self handleScanViewRefreshRequest]; + } } - (void)applicationWillTerminate:(UIApplication *)application @@ -147,14 +173,8 @@ - (void)scanForMeters NSArray* services = [NSArray arrayWithObjects:[BLEUtility expandToMooshimUUID:METER_SERVICE_UUID], [BLEUtility expandToMooshimUUID:OAD_SERVICE_UUID], [CBUUID UUIDWithData:[NSData dataWithBytes:&tmp length:2]], nil]; NSLog(@"Refresh requested"); - // [self.scan_vc.refreshControl beginRefreshing]; - NSTimer* refresh_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.scan_vc selector:@selector(reloadData) userInfo:nil repeats:YES]; - //self.scan_vc.title = @"Scan in progress..."; - - // Uncommented by Jianying Shi - // self.nav.navigationItem.title = @"Scan in progress..."; [c scanForPeripheralsByInterval:5 services:services options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES} @@ -163,19 +183,27 @@ - (void)scanForMeters [refresh_timer invalidate]; [self.scan_vc.refreshControl endRefreshing]; [self.scan_vc reloadData]; - - // Uncommented by Jianying Shi - // self.nav.navigationItem.title = @"Pull down to scan"; }]; } #pragma mark ScanViewDelegate -(void)handleScanViewRefreshRequest { + + // By Jianying Shi. + // Check if application is in background, no need to check + UIApplicationState state = [[UIApplication sharedApplication] applicationState]; + if( state == UIApplicationStateBackground || state == UIApplicationStateInactive ) + { + //Do checking here. + return; + } + [self scanForMeters]; } -(void)handleScanViewSelect:(LGPeripheral*)p { + switch( p.cbPeripheral.state ) { case CBPeripheralStateConnected:{ // We selected one that's already connected, disconnect @@ -201,7 +229,9 @@ -(void)handleScanViewSelect:(LGPeripheral*)p { } [g_meter connect]; - [self.scan_vc reloadData]; + + // [self.scan_vc reloadData]; + break; } } @@ -267,6 +297,10 @@ -(void)switchToMeterView { #pragma mark MooshimeterDeviceDelegate -(void)finishedMeterSetup { + + // Delete SVProgressHUD, if needed. + [SVProgressHUD dismiss]; + NSLog(@"Finished meter setup"); [self.scan_vc reloadData]; if( g_meter->oad_mode ) { @@ -310,9 +344,18 @@ -(void)meterDisconnected { [self.rssi_label setText:@""]; [NSTimer cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateRSSI) object:nil]; - // [self.nav popToViewController:self.scan_vc animated:YES]; + // By Jianying Shi + // Setup Portrait Orientation + NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait]; + [[UIDevice currentDevice] setValue:value forKey:@"orientation"]; + + // Check if current view controller is scan view controller + // if ( [self.nav.viewControllers[0] isKindOfClass:[ScanSettingsView class]] == false ) // Current view is ScanView, not need to pop + [self.nav popToViewController:self.scan_vc animated:YES]; + + // No need??? Jianying Shi + // [self.scan_vc reloadData]; - [self.scan_vc reloadData]; g_meter = nil; } @@ -359,4 +402,25 @@ -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)button } } +#pragma mark TopViewController Judge Part +- (UIViewController*) topViewController { + + return [self topViewController : self.window.rootViewController]; +} + +- (UIViewController*) topViewController : (UIViewController*) rootViewController { + + if( rootViewController.presentedViewController == nil ) { + return rootViewController; + } + else if( [rootViewController.presentedViewController isKindOfClass:[UINavigationController class]] ) + { + UINavigationController* navController = (UINavigationController*) rootViewController.presentedViewController; + UIViewController* lastVC = (UIViewController*) navController.viewControllers.lastObject; + return [self topViewController:lastVC]; + } + + return (UIViewController*) rootViewController.presentedViewController; +} + @end diff --git a/Mooshimeter/GraphViewController.m b/Mooshimeter/GraphViewController.m index 52bd454..70c6b47 100644 --- a/Mooshimeter/GraphViewController.m +++ b/Mooshimeter/GraphViewController.m @@ -47,6 +47,11 @@ -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOr } -(void) viewDidAppear:(BOOL)animated { + + // By Jianying Shi. + // Null Check + + [super viewDidAppear:animated]; self.navigationController.navigationBar.hidden = YES; self.config_view = nil; @@ -79,6 +84,11 @@ -(void)handleBackgroundTap { } -(void)startTrendView { + // By Jianying Shi + // Null Check + if( g_meter == nil ) + return; + g_meter->meter_settings.rw.calc_settings &=~(METER_CALC_SETTINGS_ONESHOT); g_meter->meter_settings.rw.calc_settings |= METER_CALC_SETTINGS_MEAN|METER_CALC_SETTINGS_MS; g_meter->meter_settings.rw.target_meter_state = METER_RUNNING; @@ -99,6 +109,15 @@ -(void)startTrendView { } -(void) pause { + + // By Jianying + // Null check + if( g_meter == nil ) + { + NSLog(@"Meter already disconnected, igonring request"); + return; + } + self->play = NO; g_meter->meter_settings.rw.target_meter_state = METER_PAUSED; [g_meter sendMeterSettings:^(NSError *error) { @@ -138,6 +157,15 @@ -(void) trendViewUpdate { } -(void) redrawTrendView:(NSTimer*)timer { + + // By Jianying Shi + // Null Check + if( g_meter == nil ) + { + NSLog(@"Invalid meter, igonoring redraw request"); + return; + } + NSLog(@"Redrawing"); CPTGraph *graph = self.hostView.hostedGraph; [graph reloadData]; diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index 3502c12..31ac13b 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 573 + 650 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 11de541..7a3ec53 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -27,6 +27,9 @@ #import "Vendor/MPPlot/MPPlot.h" #import "Vendor/MPPlot/MPGraphView.h" #import "Vendor/MPPlot/MPBarsGraphView.h" +#import "Vendor/SVProgressHUD/SVProgressHUD.h" + +#import "AppDelegate.h" #import #include @@ -70,21 +73,42 @@ -(void)reloadData { // Auto - connect logics. // Jianying Shi -#if 0 - NSString* savedUUID = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUID"]; - if( savedUUID != nil && savedUUID.length > 0 ) + AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; + NSString* backUUID = delegate.mlastConnectedUDID; + if( backUUID != nil && backUUID.length > 0 ) { for( NSInteger i = 0; i < self.peripherals.count; i ++ ) { LGPeripheral* p = self.peripherals[i]; - if( [[p UUIDString] isEqualToString : savedUUID] == YES ) + if( [[p UUIDString] isEqualToString : backUUID] == YES ) { + delegate.mlastConnectedUDID = nil; + [self.delegate handleScanViewSelect:p]; break; } } } + else + { +#if 0 + NSArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUIDs"]; + if( savedUUIDs!= nil && savedUUIDs.count > 0 ) + { + for( NSInteger i = 0; i < self.peripherals.count; i ++ ) + { + LGPeripheral* p = self.peripherals[i]; + + if( [savedUUIDs containsObject:p.UUIDString] == YES ) + { + [self.delegate handleScanViewSelect:p]; + break; + } + } + } #endif + + } } @@ -126,6 +150,7 @@ -(void)viewDidAppear:(BOOL)animated // If we've appeared, disconnect whatever we were talking to. [g_meter disconnect:nil]; } + // Start a new scan for meters [self.delegate handleScanViewRefreshRequest]; } @@ -139,28 +164,6 @@ - (NSUInteger)supportedInterfaceOrientations { #pragma mark - Long-press menu - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer { - // Check if menu is currently showing. - UIView* existMenu = [self.view viewWithTag:kContextMenu_Tag]; - if( existMenu != nil ) - return; - - if (!settingsMenu) { - - settingsMenu = [KxMenu new]; - settingsMenu.menuItems = self.createSettingsMenuItems; - settingsMenu.blurredBackground = NO; - - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { - settingsMenu.blurredBackground = YES; - } - - settingsMenu.tintColor = [UIColor darkGrayColor];//[[UIColor whiteColor] colorWithAlphaComponent:0.5f]; - settingsMenu.tintColor1 = [UIColor lightGrayColor]; - - settingsMenu.selectedColor = [UIColor colorWithRed:0.9f green:0 blue:0 alpha:1.f]; - settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; - } - // Check tapped table view cell CGPoint p = [gestureRecognizer locationInView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p]; @@ -168,14 +171,6 @@ - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer NSLog(@"long press on table view but not on a row"); } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { - // Display setting menu - CGRect boundRect = CGRectMake(p.x, p.y, 5, 5); - boundRect.origin.y += 64; - - [settingsMenu setTag : indexPath.row + kContextMenu_Tag]; - [settingsMenu showMenuInView:self.tableView - fromRect:boundRect]; - // Display auto-connect checked state ScanTableViewCell* c = (ScanTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath]; LGPeripheral* peripheral = c.p; @@ -183,28 +178,54 @@ - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer NSMutableArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUIDs"]; NSString* deviceUUID = [peripheral UUIDString]; - KxMenuItem* checkItem = settingsMenu.menuItems[2]; - + BOOL bChecked = NO; if( savedUUIDs != nil && savedUUIDs.count > 0 ) { - for(int i = 0; i < savedUUIDs.count; i ++) { - if( [[savedUUIDs objectAtIndex:i] isEqualToString:deviceUUID] ) - { - checkItem.image = [UIImage imageNamed:@"checkicon"]; - break; - } - } + if ( [savedUUIDs containsObject:deviceUUID] == true ) + bChecked = YES; } - else { - checkItem.image = nil; + + // if (!settingsMenu) + { + + settingsMenu = [KxMenu new]; + settingsMenu.menuItems = [self createSettingsMenuItems:bChecked]; + settingsMenu.blurredBackground = NO; + + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { + settingsMenu.blurredBackground = YES; + } + + settingsMenu.tintColor = [UIColor darkGrayColor];//[[UIColor whiteColor] colorWithAlphaComponent:0.5f]; + settingsMenu.tintColor1 = [UIColor lightGrayColor]; + + settingsMenu.selectedColor = [UIColor colorWithRed:0.9f green:0 blue:0 alpha:1.f]; + settingsMenu.selectedColor1 = [UIColor colorWithRed:0.8f green:0 blue:0 alpha:1.f]; } + + + // Display setting menu + CGRect boundRect = CGRectMake(p.x, p.y, 5, 5); + boundRect.origin.y += 64; + + [settingsMenu setTag : indexPath.row + kContextMenu_Tag]; + [settingsMenu showMenuInView:self.tableView + fromRect:boundRect]; + } else { NSLog(@"gestureRecognizer.state = %d", gestureRecognizer.state); } } -- (NSArray*) createSettingsMenuItems { +- (NSArray*) createSettingsMenuItems : (BOOL) bChecked { + + KxMenuItem* checkItem = nil; + if( bChecked ) + checkItem = [KxMenuItem menuItem:@"Auto Connect" image:[UIImage imageNamed:@"checkicon"] target:self action:@selector(autoConnectPressed:)]; + else + checkItem = [KxMenuItem menuItem:@"Auto Connect" image:nil target:self action:@selector(autoConnectPressed:)]; + NSArray *menuItems = @[ @@ -218,10 +239,7 @@ - (NSArray*) createSettingsMenuItems { target:self action:@selector(firmwareUpdatePressed:)], - [KxMenuItem menuItem:@"Auto Connect" - image:[UIImage imageNamed:@"checkicon"] - target:self - action:@selector(autoConnectPressed:)], + checkItem, ]; KxMenuItem *first = menuItems[0]; @@ -258,13 +276,22 @@ - (void) autoConnectPressed : (id) sender { NSLog(@"Auto connect"); - NSString* deviceUUID = [g_meter.p UUIDString]; + // Get current selected peripheral + NSInteger selectedRow = [settingsMenu tag] - kContextMenu_Tag; + LGPeripheral* p = [self.peripherals objectAtIndex:selectedRow]; + + NSString* deviceUUID = p.UUIDString; if( deviceUUID ) { // Save current device UUID to user defaults NSMutableArray* savedUUIDs = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:@"autoUUIDs"]; if( savedUUIDs != nil ) // already has saved id - [savedUUIDs addObject:deviceUUID]; + { + if( [savedUUIDs containsObject:deviceUUID] == YES ) // It means, this device already saved, but user want's to disable auto-connect + [savedUUIDs removeObject:deviceUUID]; + else + [savedUUIDs addObject:deviceUUID]; + } else { savedUUIDs = [[NSMutableArray alloc] initWithObjects:deviceUUID, nil]; } From 10ab0d0da537abb5eeffe3e4c9cbc95be8473c7e Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Sun, 10 May 2015 15:17:26 +0800 Subject: [PATCH 7/9] implemented app lifecycle task, auto-connect submenu function. --- LGBluetooth/LGPeripheral.h | 6 ++ Mooshimeter/AppDelegate.m | 16 +++-- Mooshimeter/MeterViewController.m | 4 +- Mooshimeter/MooshimeterDevice.m | 6 +- Mooshimeter/ScanViewController.h | 2 + Mooshimeter/ScanViewController.m | 100 +++++++++++++++++++----------- 6 files changed, 87 insertions(+), 47 deletions(-) diff --git a/LGBluetooth/LGPeripheral.h b/LGBluetooth/LGPeripheral.h index 58b7d87..391e284 100755 --- a/LGBluetooth/LGPeripheral.h +++ b/LGBluetooth/LGPeripheral.h @@ -131,6 +131,12 @@ typedef void(^LGPeripheralRSSIValueCallback)(NSNumber *RSSI, NSError *error); */ @property (strong, nonatomic) NSDictionary *advertisingData; +// Added by Jianying Shi +/** + * Represent if current device was auto-connected in the past + */ +@property (nonatomic, readwrite) BOOL pastAutoConnected; + #pragma mark - Public Methods - /** diff --git a/Mooshimeter/AppDelegate.m b/Mooshimeter/AppDelegate.m index 3a51565..dc2cf94 100644 --- a/Mooshimeter/AppDelegate.m +++ b/Mooshimeter/AppDelegate.m @@ -181,8 +181,10 @@ - (void)scanForMeters completion:^(NSArray *peripherals) { NSLog(@"Found: %d", (int)peripherals.count); [refresh_timer invalidate]; - [self.scan_vc.refreshControl endRefreshing]; [self.scan_vc reloadData]; + // By Jianying Shi + // Auto-Connect + [self.scan_vc performAutoConnect]; }]; } @@ -196,6 +198,7 @@ -(void)handleScanViewRefreshRequest { if( state == UIApplicationStateBackground || state == UIApplicationStateInactive ) { //Do checking here. + NSLog(@"App is background mode = %d", state); return; } @@ -206,6 +209,7 @@ -(void)handleScanViewSelect:(LGPeripheral*)p { switch( p.cbPeripheral.state ) { case CBPeripheralStateConnected:{ + NSLog(@"Already connected, igonre connection request & disconnecting..."); // We selected one that's already connected, disconnect [p disconnectWithCompletion:^(NSError *error) { [self meterDisconnected]; @@ -230,8 +234,8 @@ -(void)handleScanViewSelect:(LGPeripheral*)p { [g_meter connect]; + // Commented by Jianying Shi // [self.scan_vc reloadData]; - break; } } @@ -302,7 +306,10 @@ -(void)finishedMeterSetup { [SVProgressHUD dismiss]; NSLog(@"Finished meter setup"); - [self.scan_vc reloadData]; + + // ??? Commented By Jianying Shi + // [self.scan_vc reloadData]; + if( g_meter->oad_mode ) { // We connected to a meter in OAD mode as requested previously. Update firmware. NSLog(@"Connected in OAD mode"); @@ -350,8 +357,7 @@ -(void)meterDisconnected { [[UIDevice currentDevice] setValue:value forKey:@"orientation"]; // Check if current view controller is scan view controller - // if ( [self.nav.viewControllers[0] isKindOfClass:[ScanSettingsView class]] == false ) // Current view is ScanView, not need to pop - [self.nav popToViewController:self.scan_vc animated:YES]; + [self.nav popToViewController:self.scan_vc animated:YES]; // No need??? Jianying Shi // [self.scan_vc reloadData]; diff --git a/Mooshimeter/MeterViewController.m b/Mooshimeter/MeterViewController.m index 29e51cc..ffba0cf 100644 --- a/Mooshimeter/MeterViewController.m +++ b/Mooshimeter/MeterViewController.m @@ -87,6 +87,7 @@ - (void)viewDidLoad [v addSubview:sv]; [self.view addSubview:v]; + // By Jianying Shi // Trying to customized back button UIBarButtonItem* back_item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToScanView:)]; self.navigationItem.leftBarButtonItem = back_item; @@ -94,7 +95,8 @@ - (void)viewDidLoad - (void) backToScanView : (id) sender { - [self.navigationController popViewControllerAnimated:YES]; + [g_meter disconnect:nil]; + // [self.navigationController popViewControllerAnimated:YES]; } #pragma mark - View lifey cycle diff --git a/Mooshimeter/MooshimeterDevice.m b/Mooshimeter/MooshimeterDevice.m index 1a201ec..9df10a5 100644 --- a/Mooshimeter/MooshimeterDevice.m +++ b/Mooshimeter/MooshimeterDevice.m @@ -83,11 +83,7 @@ -(void)connect { [self.p discoverServicesWithCompletion:^(NSArray *services, NSError *error) { for (LGService *service in services) { - NSString* meterServiceUUID = [BLEUtility expandToMooshimUUIDString:METER_SERVICE_UUID]; - - //NSLog(@"service UUID = %@", service.UUIDString); - //NSLog(@"meterSevice UUID = %@", service.UUIDString); - + NSString* meterServiceUUID = [BLEUtility expandToMooshimUUIDString:METER_SERVICE_UUID]; if([service.UUIDString isEqualToString : meterServiceUUID]) { NSLog(@"METER SERVICE FOUND. Discovering characteristics."); self->oad_mode = NO; diff --git a/Mooshimeter/ScanViewController.h b/Mooshimeter/ScanViewController.h index f9d74c3..a726872 100644 --- a/Mooshimeter/ScanViewController.h +++ b/Mooshimeter/ScanViewController.h @@ -42,4 +42,6 @@ along with this program. If not, see . -(instancetype)initWithDelegate:(id)d; -(void)reloadData; +-(void) performAutoConnect; + @end diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 7a3ec53..364174b 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -43,11 +43,16 @@ #define Round(a) (NSInteger) (a + 0.5) +#define kAutoConnectDictKey @"autoUUIDs" + @interface ScanViewController () { NSMutableArray *_objects; KxMenu* settingsMenu; } + +-(BOOL) connectToPeripheral : (NSString*) uuidString withPeripherals : (NSArray*)peripheral_array; + @end @implementation ScanViewController @@ -64,52 +69,68 @@ - (void)awakeFromNib [super awakeFromNib]; } +// By Jianying Shi +-(BOOL) connectToPeripheral : (NSString*) uuidString withPeripherals : (NSArray*)peripheral_array { + + if (uuidString == nil || [uuidString length] < 1) { + return false; + } + + if (peripheral_array == nil || peripheral_array.count < 1 ) { + return false; + } + + for( LGPeripheral* p in peripheral_array ) + { + if( [[p UUIDString] isEqualToString : uuidString] == YES ) + { + [self.delegate handleScanViewSelect:p]; + return true; + } + } + + return false; +} + -(void)reloadData { + NSLog(@"Reload requested"); LGCentralManager* c = [LGCentralManager sharedInstance]; self.peripherals = [c.peripherals copy]; [self.tableView reloadData]; - - // Auto - connect logics. - // Jianying Shi +} + +// By Jianying Shi +// Auto - connect logics. +-(void) performAutoConnect { AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; - NSString* backUUID = delegate.mlastConnectedUDID; - if( backUUID != nil && backUUID.length > 0 ) - { - for( NSInteger i = 0; i < self.peripherals.count; i ++ ) - { - LGPeripheral* p = self.peripherals[i]; - if( [[p UUIDString] isEqualToString : backUUID] == YES ) - { - delegate.mlastConnectedUDID = nil; - - [self.delegate handleScanViewSelect:p]; - break; - } - } - } - else - { -#if 0 - NSArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUIDs"]; - if( savedUUIDs!= nil && savedUUIDs.count > 0 ) - { - for( NSInteger i = 0; i < self.peripherals.count; i ++ ) + if( [self connectToPeripheral:delegate.mlastConnectedUDID withPeripherals:self.peripherals] == YES ) // Restoring connection for background + + delegate.mlastConnectedUDID = nil; + else { + + NSArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : kAutoConnectDictKey]; + if ( savedUUIDs != nil && savedUUIDs.count > 0 ) { + + for( LGPeripheral* p in self.peripherals ) { - LGPeripheral* p = self.peripherals[i]; + NSLog(@"Trying to auto-connect"); - if( [savedUUIDs containsObject:p.UUIDString] == YES ) + if( [savedUUIDs containsObject:p.UUIDString] == YES && p.cbPeripheral.state == CBPeripheralStateDisconnected && p.pastAutoConnected == NO ) { + // Check if this device was auto-connected in the past + [SVProgressHUD showWithStatus:@"Auto-connecting. Please wait for a second"]; + + // Set Flag for Auto-Connect + p.pastAutoConnected = YES; + [self.delegate handleScanViewSelect:p]; break; } } } -#endif - } - } // by Jianying Shi. @@ -146,10 +167,12 @@ -(void)viewDidAppear:(BOOL)animated // [self setTitle:@"Swipe down to scan"] // [self setTitle:@"Tap Scan Button"]; +#if 0 if(g_meter) { // If we've appeared, disconnect whatever we were talking to. [g_meter disconnect:nil]; } +#endif // Start a new scan for meters [self.delegate handleScanViewRefreshRequest]; @@ -175,7 +198,7 @@ - (void) longpressScanSetting :(UILongPressGestureRecognizer *)gestureRecognizer ScanTableViewCell* c = (ScanTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath]; LGPeripheral* peripheral = c.p; - NSMutableArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : @"autoUUIDs"]; + NSMutableArray* savedUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey : kAutoConnectDictKey]; NSString* deviceUUID = [peripheral UUIDString]; BOOL bChecked = NO; @@ -284,19 +307,24 @@ - (void) autoConnectPressed : (id) sender if( deviceUUID ) { // Save current device UUID to user defaults - NSMutableArray* savedUUIDs = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:@"autoUUIDs"]; + NSMutableArray* savedUUIDs = (NSMutableArray*)[[NSUserDefaults standardUserDefaults] objectForKey:kAutoConnectDictKey]; + if( savedUUIDs != nil ) // already has saved id { - if( [savedUUIDs containsObject:deviceUUID] == YES ) // It means, this device already saved, but user want's to disable auto-connect - [savedUUIDs removeObject:deviceUUID]; + NSMutableArray* mutaleArray = [NSMutableArray arrayWithArray:savedUUIDs]; + + if( [mutaleArray containsObject:deviceUUID] == YES ) // It means, this device already saved, but user want's to disable auto-connect + [mutaleArray removeObject:deviceUUID]; else - [savedUUIDs addObject:deviceUUID]; + [mutaleArray addObject:deviceUUID]; + + savedUUIDs = mutaleArray; } else { savedUUIDs = [[NSMutableArray alloc] initWithObjects:deviceUUID, nil]; } - [[NSUserDefaults standardUserDefaults] setObject:savedUUIDs forKey:@"autoUUIDs"]; + [[NSUserDefaults standardUserDefaults] setObject:savedUUIDs forKey:kAutoConnectDictKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } From 3d37ac662c24598c3fb36c0cc0a6d4ea300105aa Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Thu, 14 May 2015 06:55:42 +0800 Subject: [PATCH 8/9] menu appear delay 1s, disappear delay 0.2 --- Mooshimeter/ScanViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mooshimeter/ScanViewController.m b/Mooshimeter/ScanViewController.m index 364174b..ba08267 100644 --- a/Mooshimeter/ScanViewController.m +++ b/Mooshimeter/ScanViewController.m @@ -155,7 +155,7 @@ - (void)viewDidLoad // Long-press table-view handler UILongPressGestureRecognizer *longpressScanSetting = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longpressScanSetting:)]; - longpressScanSetting.minimumPressDuration = 2.0; + longpressScanSetting.minimumPressDuration = 1.0; longpressScanSetting.delegate = self; [self.tableView addGestureRecognizer:longpressScanSetting]; From 00156daa5ff224480b9ca97f94894ca94775e850 Mon Sep 17 00:00:00 2001 From: Jianying Shi Date: Tue, 2 Jun 2015 04:50:45 +0800 Subject: [PATCH 9/9] fixed Fix network download delay on bootup issue --- Mooshimeter.xcodeproj/project.pbxproj | 24 ++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 296 +----------------- Mooshimeter/MeterViewController.m | 10 +- Mooshimeter/Mooshimeter-Info.plist | 2 +- Mooshimeter/OADProfile.h | 4 + Mooshimeter/OADProfile.m | 54 +++- Mooshimeter/Reachability/Reachability.h | 91 ++++++ Mooshimeter/Reachability/Reachability.m | 285 +++++++++++++++++ Mooshimeter/ReachabilityManager.h | 20 ++ Mooshimeter/ReachabilityManager.m | 118 +++++++ Mooshimeter/Vendor/KxMenu/KxMenu.m | 6 +- .../Vendor/Reachability/Reachability.h | 91 ++++++ .../Vendor/Reachability/Reachability.m | 284 +++++++++++++++++ 13 files changed, 985 insertions(+), 300 deletions(-) create mode 100755 Mooshimeter/Reachability/Reachability.h create mode 100755 Mooshimeter/Reachability/Reachability.m create mode 100644 Mooshimeter/ReachabilityManager.h create mode 100644 Mooshimeter/ReachabilityManager.m create mode 100755 Mooshimeter/Vendor/Reachability/Reachability.h create mode 100755 Mooshimeter/Vendor/Reachability/Reachability.m diff --git a/Mooshimeter.xcodeproj/project.pbxproj b/Mooshimeter.xcodeproj/project.pbxproj index f008990..970cf61 100644 --- a/Mooshimeter.xcodeproj/project.pbxproj +++ b/Mooshimeter.xcodeproj/project.pbxproj @@ -48,6 +48,8 @@ 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF4D8DC1A2BCD2600622F58 /* MeterSettingsView.m */; }; 842D9F641AE423B300ABBB89 /* LDProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F631AE423B200ABBB89 /* LDProgressView.m */; }; 842D9F671AE424ED00ABBB89 /* UIColor+RGBValues.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */; }; + 8457CBB11B1CF64200572CFE /* ReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8457CBB01B1CF64200572CFE /* ReachabilityManager.m */; }; + 8457CBB51B1CF69F00572CFE /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 8457CBB41B1CF69F00572CFE /* Reachability.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 846403F21AF5603A002C78FF /* _MPWButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403E71AF5603A002C78FF /* _MPWButton.m */; }; 846403F31AF5603A002C78FF /* MPBarsGraphView.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403E91AF5603A002C78FF /* MPBarsGraphView.m */; }; 846403F41AF5603A002C78FF /* MPGraphView.m in Sources */ = {isa = PBXBuildFile; fileRef = 846403EB1AF5603A002C78FF /* MPGraphView.m */; }; @@ -222,6 +224,10 @@ 842D9F631AE423B200ABBB89 /* LDProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDProgressView.m; sourceTree = ""; }; 842D9F651AE424ED00ABBB89 /* UIColor+RGBValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+RGBValues.h"; sourceTree = ""; }; 842D9F661AE424ED00ABBB89 /* UIColor+RGBValues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+RGBValues.m"; sourceTree = ""; }; + 8457CBAF1B1CF64200572CFE /* ReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ReachabilityManager.h; path = Mooshimeter/ReachabilityManager.h; sourceTree = ""; }; + 8457CBB01B1CF64200572CFE /* ReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ReachabilityManager.m; path = Mooshimeter/ReachabilityManager.m; sourceTree = ""; }; + 8457CBB31B1CF69F00572CFE /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; + 8457CBB41B1CF69F00572CFE /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 846403E61AF5603A002C78FF /* _MPWButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _MPWButton.h; sourceTree = ""; }; 846403E71AF5603A002C78FF /* _MPWButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _MPWButton.m; sourceTree = ""; }; 846403E81AF5603A002C78FF /* MPBarsGraphView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPBarsGraphView.h; sourceTree = ""; }; @@ -542,6 +548,8 @@ 4ABE54191A080A9000277456 /* OADProfile.m */, 4ABE541C1A080A9000277456 /* OADProgressViewController.h */, 4ABE541D1A080A9000277456 /* OADProgressViewController.m */, + 8457CBAF1B1CF64200572CFE /* ReachabilityManager.h */, + 8457CBB01B1CF64200572CFE /* ReachabilityManager.m */, ); name = OAD; sourceTree = ""; @@ -567,6 +575,16 @@ path = Mooshimeter/Vendor/LDProgressView; sourceTree = SOURCE_ROOT; }; + 8457CBB21B1CF69F00572CFE /* Reachability */ = { + isa = PBXGroup; + children = ( + 8457CBB31B1CF69F00572CFE /* Reachability.h */, + 8457CBB41B1CF69F00572CFE /* Reachability.m */, + ); + name = Reachability; + path = Mooshimeter/Vendor/Reachability; + sourceTree = SOURCE_ROOT; + }; 846403E51AF5603A002C78FF /* MPPlot */ = { isa = PBXGroup; children = ( @@ -601,6 +619,7 @@ 847A13941AE3B27800E36E17 /* Vendor */ = { isa = PBXGroup; children = ( + 8457CBB21B1CF69F00572CFE /* Reachability */, 846403E51AF5603A002C78FF /* MPPlot */, 847121911AE61AC400035BB2 /* SVProgressHUD */, 842D9F611AE423B200ABBB89 /* LDProgressView */, @@ -763,6 +782,7 @@ 847A13A21AE3B94900E36E17 /* UIImage+ImageEffects.m in Sources */, 4ACF8AE11A0C918B00EC402B /* SmartNavigationController.m in Sources */, 846403F41AF5603A002C78FF /* MPGraphView.m in Sources */, + 8457CBB11B1CF64200572CFE /* ReachabilityManager.m in Sources */, 4AF4D8DD1A2BCD2600622F58 /* MeterSettingsView.m in Sources */, 0C63853D17D694DC00D3FB31 /* AppDelegate.m in Sources */, 846403F61AF5603A002C78FF /* UIBezierPath+curved.m in Sources */, @@ -772,6 +792,7 @@ 4AA9EA2F1A12B54E0031C12B /* LGPeripheral.m in Sources */, 846403F71AF5603A002C78FF /* UIView+Frame.m in Sources */, 4AA9EA2E1A12B54E0031C12B /* LGCharacteristic.m in Sources */, + 8457CBB51B1CF69F00572CFE /* Reachability.m in Sources */, 0C63854F17D694DC00D3FB31 /* MeterViewController.m in Sources */, 4AA9EA311A12B54E0031C12B /* LGUtils.m in Sources */, 0C78962417D7E56B00D1439E /* BLEUtility.m in Sources */, @@ -904,6 +925,7 @@ GCC_PREFIX_HEADER = "Mooshimeter/Mooshimeter-Prefix.pch"; INFOPLIST_FILE = "Mooshimeter/Mooshimeter-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", @@ -918,6 +940,7 @@ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "0b2a1bee-e707-40a3-b180-a4b90b179733"; SDKROOT = iphoneos; SEPARATE_STRIP = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; @@ -936,6 +959,7 @@ GCC_PREFIX_HEADER = "Mooshimeter/Mooshimeter-Prefix.pch"; INFOPLIST_FILE = "Mooshimeter/Mooshimeter-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", diff --git a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 2a086e6..ea51c70 100644 --- a/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Mooshimeter.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -59,20 +59,6 @@ endingLineNumber = "37"> - - - - + offsetFromSymbolStart = "425"> + offsetFromSymbolStart = "441"> @@ -151,102 +137,6 @@ endingLineNumber = "266"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + startingLineNumber = "421" + endingLineNumber = "421" + landmarkName = "-performAction:" + landmarkType = "5"> diff --git a/Mooshimeter/MeterViewController.m b/Mooshimeter/MeterViewController.m index ffba0cf..01e3676 100644 --- a/Mooshimeter/MeterViewController.m +++ b/Mooshimeter/MeterViewController.m @@ -17,9 +17,10 @@ ***************************/ #import "MeterViewController.h" - #import "Vendor/KxMenu/KxMenu.h" +#import "MooshimeterDeviceSimulator.h" + dispatch_semaphore_t tmp_sem; #define kContextMenu_Tag 2000 @@ -96,7 +97,12 @@ - (void)viewDidLoad - (void) backToScanView : (id) sender { [g_meter disconnect:nil]; - // [self.navigationController popViewControllerAnimated:YES]; + + // Add exception for Simulator + // By Jianying Shi + if ([g_meter isKindOfClass:[MooshimeterDeviceSimulator class]] == true) { + [self.navigationController popViewControllerAnimated:YES]; + } } #pragma mark - View lifey cycle diff --git a/Mooshimeter/Mooshimeter-Info.plist b/Mooshimeter/Mooshimeter-Info.plist index 31ac13b..1272246 100644 --- a/Mooshimeter/Mooshimeter-Info.plist +++ b/Mooshimeter/Mooshimeter-Info.plist @@ -25,7 +25,7 @@ CFBundleSignature ???? CFBundleVersion - 650 + 681 LSRequiresIPhoneOS UIMainStoryboardFile~ipad diff --git a/Mooshimeter/OADProfile.h b/Mooshimeter/OADProfile.h index ec0ef63..d719dab 100644 --- a/Mooshimeter/OADProfile.h +++ b/Mooshimeter/OADProfile.h @@ -25,6 +25,10 @@ typedef void (^FirmwareUpdateFinishCallback)(NSError *error); @property (strong,nonatomic) NSData *imageData; +// Added by Jianying Shi +@property BOOL image_downloaded; +@property NSString* download_imagePath; + @property int nBlocks; @property int nBytes; @property int iBlocks; diff --git a/Mooshimeter/OADProfile.m b/Mooshimeter/OADProfile.m index db89651..eef7d1f 100644 --- a/Mooshimeter/OADProfile.m +++ b/Mooshimeter/OADProfile.m @@ -9,7 +9,7 @@ #import "OADProfile.h" #import "BLEUtility.h" - +#import "ReachabilityManager.h" @implementation OADProfile @@ -19,21 +19,48 @@ -(id) init:(NSString*) filename { self.canceled = FALSE; self.inProgramming = FALSE; self.start = YES; - NSString *stringURL = @"https://moosh.im/s/f/mooshimeter-firmware-latest.bin"; - NSURL *url = [NSURL URLWithString:stringURL]; - self.imageData = [NSData dataWithContentsOfURL:url]; - NSLog(@"Loaded firmware \"%@\"of size : %d",filename,(int)self.imageData.length); - if(self.imageData.length==0) { - // We failed to load the firmware. Should we do something? - self->imageHeader.build_time=0; - } else { - [self.imageData getBytes:&self->imageHeader length:sizeof(img_hdr_t)]; + + // By Jianying + self.download_imagePath = filename; + self.image_downloaded = false; + + // Add complete block + [ReachabilityManager sharedInstance]; + + if( [[ReachabilityManager sharedInstance] connectedToInternet] == true ) { + [self downloadImage]; + } + else { + [[ReachabilityManager sharedInstance] addConnectionStatusChangeHandler:^(BOOL connectedToInternet) { + + [self downloadImage]; + } withIdentifier:@"imageDownloaded"]; } } return self; } +-(void) downloadImage { + + NSString *stringURL = @"https://moosh.im/s/f/mooshimeter-firmware-latest.bin"; + NSURL *url = [NSURL URLWithString:stringURL]; + self.imageData = [NSData dataWithContentsOfURL:url]; + NSLog(@"Loaded firmware \"%@\"of size : %d",self.download_imagePath,(int)self.imageData.length); + if(self.imageData.length==0) { + // We failed to load the firmware. Should we do something? + self->imageHeader.build_time=0; + } else { + [self.imageData getBytes:&self->imageHeader length:sizeof(img_hdr_t)]; + } + + self.image_downloaded = true; + + // Once download become success, delete handler + [[ReachabilityManager sharedInstance] removeConnectionStatusChangeHandlerWithIdentifier:@"imageDownloaded"]; +} + -(void) startUpload { + NSLog(@"Configuring OAD Profile"); self.start = YES; LGCharacteristic* image_notify = [g_meter getLGChar:OAD_IMAGE_NOTIFY]; @@ -73,6 +100,13 @@ -(void)handleDisconnect:(NSNotification *) notification { } -(void) uploadImage { + + // By Jianying Shi + // If image is not downloaded yet, skip upload Image to device + if(self.image_downloaded == false) { + return; + } + self.inProgramming = YES; self.canceled = NO; diff --git a/Mooshimeter/Reachability/Reachability.h b/Mooshimeter/Reachability/Reachability.h new file mode 100755 index 0000000..aa9244f --- /dev/null +++ b/Mooshimeter/Reachability/Reachability.h @@ -0,0 +1,91 @@ +/* + + File: Reachability.h + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity 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, + ReachableViaWiFi, + ReachableViaWWAN +} NetworkStatus; +#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" + +@interface Reachability: NSObject +{ + BOOL localWiFiRef; + SCNetworkReachabilityRef reachabilityRef; +} + +//reachabilityWithHostName- Use to check the reachability of a particular host name. ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName; + +//reachabilityWithAddress- Use to check the reachability of a particular IP address. ++ (Reachability*) 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 ++ (Reachability*) reachabilityForInternetConnection; + +//reachabilityForLocalWiFi- checks whether a local wifi connection is available. ++ (Reachability*) reachabilityForLocalWiFi; + ++ (BOOL)isNetAvailable; + +//Start listening for reachability notifications on the current run loop +- (BOOL) startNotifier; +- (void) stopNotifier; + +- (NetworkStatus) currentReachabilityStatus; +//WWAN may be available, but not active until a connection has been established. +//WiFi may require a connection for VPN on Demand. +- (BOOL) connectionRequired; +@end + + diff --git a/Mooshimeter/Reachability/Reachability.m b/Mooshimeter/Reachability/Reachability.m new file mode 100755 index 0000000..1abc7c4 --- /dev/null +++ b/Mooshimeter/Reachability/Reachability.m @@ -0,0 +1,285 @@ +/* + + File: Reachability.m + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity 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 +#import +#import +#import + +#import + +#import "Reachability.h" +#import "KNDefines.h" + +#define kShouldPrintReachabilityFlags 0 + +static BOOL netAvailable = YES; + +static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) +{ +#if kShouldPrintReachabilityFlags + + DebugNSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', + comment + ); +#endif +} + + +@implementation Reachability +static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); + + //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively + // in case someon uses the Reachablity object in a different thread. + NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init]; + + Reachability* noteObject = (Reachability*) info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; + + [myPool release]; +} + +- (BOOL) startNotifier +{ + BOOL retVal = NO; + SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; + if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) + { + if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) + { + retVal = YES; + } + } + return retVal; +} + +- (void) stopNotifier +{ + if(reachabilityRef!= NULL) + { + SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + +- (void) dealloc +{ + [self stopNotifier]; + if(reachabilityRef) CFRelease(reachabilityRef); + [super dealloc]; +} + ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName +{ + Reachability* retVal = NULL; + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + Reachability* retVal = NULL; + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityForInternetConnection +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + return [self reachabilityWithAddress: &zeroAddress]; +} + ++ (Reachability*) reachabilityForLocalWiFi +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; + if(retVal!= NULL) + { + retVal->localWiFiRef = YES; + } + return retVal; +} + + + ++ (BOOL)isNetAvailable { + return netAvailable; +} + + +#pragma mark Network Flag Handling + +- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); + + BOOL retVal = NotReachable; + if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) + { + retVal = ReachableViaWiFi; + } + return retVal; +} + +- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "networkStatusForFlags"); + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) + { + // if target host is not reachable + return NotReachable; + } + + BOOL retVal = NotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) + { + // if target host is reachable and no connection is required + // then we'll assume (for now) that your on Wi-Fi + retVal = ReachableViaWiFi; + } + + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) + { + // ... and the connection is on-demand (or on-traffic) if the + // calling application is using the CFSocketStream or higher APIs + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + // ... and no [user] intervention is needed + retVal = ReachableViaWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + // ... but WWAN connections are OK if the calling application + // is using the CFNetwork (CFSocketStream?) APIs. + retVal = ReachableViaWWAN; + } + return retVal; +} + +- (BOOL) connectionRequired; +{ + NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + return NO; +} + +- (NetworkStatus) currentReachabilityStatus +{ + NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); + NetworkStatus retVal = NotReachable; + SCNetworkReachabilityFlags flags; + netAvailable = NO; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + if(localWiFiRef) + { + retVal = [self localWiFiStatusForFlags: flags]; + } + else + { + retVal = [self networkStatusForFlags: flags]; + } + } + + if(retVal != NotReachable) + netAvailable = YES; + + return retVal; +} +@end diff --git a/Mooshimeter/ReachabilityManager.h b/Mooshimeter/ReachabilityManager.h new file mode 100644 index 0000000..d8ed9e8 --- /dev/null +++ b/Mooshimeter/ReachabilityManager.h @@ -0,0 +1,20 @@ +// +// ReachabilityManager.h +// Mooshimeter +// +// Created by Admin on 6/2/15. +// Copyright (c) 2015 mooshim. All rights reserved. +// + +#import +#import "Vendor/Reachability/Reachability.h" + +@interface ReachabilityManager : NSObject + ++(ReachabilityManager*)sharedInstance; + +-(BOOL)connectedToInternet; +-(void)addConnectionStatusChangeHandler:(void(^)(BOOL connectedToInternet))block withIdentifier:(NSString*)identifier; +-(void)removeConnectionStatusChangeHandlerWithIdentifier:(NSString*)identifier; + +@end diff --git a/Mooshimeter/ReachabilityManager.m b/Mooshimeter/ReachabilityManager.m new file mode 100644 index 0000000..0813f81 --- /dev/null +++ b/Mooshimeter/ReachabilityManager.m @@ -0,0 +1,118 @@ +// +// ReachabilityManager.m +// Mooshimeter +// +// Created by Admin on 6/2/15. +// Copyright (c) 2015 mooshim. All rights reserved. +// + +#import "ReachabilityManager.h" + +@interface ReachabilityManager() +{ + +} + +@property Reachability* reachability; +@property BOOL isConnectedToInternet; +@property BOOL isWaiting; +@property NSMutableDictionary* blockDictionary; +@property NSMutableArray* blockKeysArray; + +@end + +@implementation ReachabilityManager + ++(ReachabilityManager*)sharedInstance +{ + static id sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return (ReachabilityManager*)sharedInstance; +} + +-(id) init +{ + self = [super init]; + if (self) + { + self.reachability = [Reachability reachabilityForInternetConnection]; + [self.reachability startNotifier]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onConnectionStatusChange) name:kReachabilityChangedNotification object:nil]; + self.blockDictionary = [[NSMutableDictionary alloc] init]; + self.blockKeysArray = [[NSMutableArray alloc] init]; + + if([self.reachability currentReachabilityStatus] == NotReachable) + { + self.isConnectedToInternet = false; + } + else + { + self.isConnectedToInternet = true; + } + + self.isWaiting = false; + + } + return self; +} + +-(BOOL)connectedToInternet +{ + return self.isConnectedToInternet; +} + +-(void)addConnectionStatusChangeHandler:(void(^)(BOOL connectedToInternet))block withIdentifier:(NSString*)identifier +{ + if(![self.blockKeysArray containsObject:identifier]) + { + [self.blockKeysArray addObject:identifier]; + self.blockDictionary[identifier] = block; + } +} + +-(void)removeConnectionStatusChangeHandlerWithIdentifier:(NSString*)identifier +{ + if([self.blockKeysArray containsObject:identifier]) + { + [self.blockKeysArray removeObject:identifier]; + } +} + +-(void)onConnectionStatusChange +{ + if(self.isWaiting == false) + { + [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(afterRapidAlternating) userInfo:nil repeats:NO]; + } +} + +-(void)afterRapidAlternating +{ + if([self.reachability currentReachabilityStatus] == NotReachable) + { + if(self.isConnectedToInternet == true) + { + self.isConnectedToInternet = false; + for(NSString* key in self.blockKeysArray) + { + ((void(^)(BOOL))self.blockDictionary[key])(self.isConnectedToInternet); + } + } + } + else + { + if(self.isConnectedToInternet == false) + { + self.isConnectedToInternet = true; + for(NSString* key in self.blockKeysArray) + { + ((void(^)(BOOL))self.blockDictionary[key])(self.isConnectedToInternet); + } + } + } +} + +@end diff --git a/Mooshimeter/Vendor/KxMenu/KxMenu.m b/Mooshimeter/Vendor/KxMenu/KxMenu.m index c2b820e..c9e08c7 100644 --- a/Mooshimeter/Vendor/KxMenu/KxMenu.m +++ b/Mooshimeter/Vendor/KxMenu/KxMenu.m @@ -64,7 +64,11 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event UIView *touched = [[touches anyObject] view]; if (touched == self) { //[self.nextResponder touchesBegan:touches withEvent:event]; - [self.menuView dismissMenu:YES]; + // [self.menuView dismissMenu:YES]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.menuView dismissMenu:YES]; + }); } } diff --git a/Mooshimeter/Vendor/Reachability/Reachability.h b/Mooshimeter/Vendor/Reachability/Reachability.h new file mode 100755 index 0000000..aa9244f --- /dev/null +++ b/Mooshimeter/Vendor/Reachability/Reachability.h @@ -0,0 +1,91 @@ +/* + + File: Reachability.h + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity 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, + ReachableViaWiFi, + ReachableViaWWAN +} NetworkStatus; +#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" + +@interface Reachability: NSObject +{ + BOOL localWiFiRef; + SCNetworkReachabilityRef reachabilityRef; +} + +//reachabilityWithHostName- Use to check the reachability of a particular host name. ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName; + +//reachabilityWithAddress- Use to check the reachability of a particular IP address. ++ (Reachability*) 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 ++ (Reachability*) reachabilityForInternetConnection; + +//reachabilityForLocalWiFi- checks whether a local wifi connection is available. ++ (Reachability*) reachabilityForLocalWiFi; + ++ (BOOL)isNetAvailable; + +//Start listening for reachability notifications on the current run loop +- (BOOL) startNotifier; +- (void) stopNotifier; + +- (NetworkStatus) currentReachabilityStatus; +//WWAN may be available, but not active until a connection has been established. +//WiFi may require a connection for VPN on Demand. +- (BOOL) connectionRequired; +@end + + diff --git a/Mooshimeter/Vendor/Reachability/Reachability.m b/Mooshimeter/Vendor/Reachability/Reachability.m new file mode 100755 index 0000000..918855c --- /dev/null +++ b/Mooshimeter/Vendor/Reachability/Reachability.m @@ -0,0 +1,284 @@ +/* + + File: Reachability.m + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity 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 +#import +#import +#import + +#import + +#import "Reachability.h" + +#define kShouldPrintReachabilityFlags 0 + +static BOOL netAvailable = YES; + +static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) +{ +#if kShouldPrintReachabilityFlags + + DebugNSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', + comment + ); +#endif +} + + +@implementation Reachability +static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); + + //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively + // in case someon uses the Reachablity object in a different thread. + NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init]; + + Reachability* noteObject = (Reachability*) info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; + + [myPool release]; +} + +- (BOOL) startNotifier +{ + BOOL retVal = NO; + SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; + if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) + { + if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) + { + retVal = YES; + } + } + return retVal; +} + +- (void) stopNotifier +{ + if(reachabilityRef!= NULL) + { + SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + +- (void) dealloc +{ + [self stopNotifier]; + if(reachabilityRef) CFRelease(reachabilityRef); + [super dealloc]; +} + ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName +{ + Reachability* retVal = NULL; + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + Reachability* retVal = NULL; + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityForInternetConnection +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + return [self reachabilityWithAddress: &zeroAddress]; +} + ++ (Reachability*) reachabilityForLocalWiFi +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; + if(retVal!= NULL) + { + retVal->localWiFiRef = YES; + } + return retVal; +} + + + ++ (BOOL)isNetAvailable { + return netAvailable; +} + + +#pragma mark Network Flag Handling + +- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); + + BOOL retVal = NotReachable; + if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) + { + retVal = ReachableViaWiFi; + } + return retVal; +} + +- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "networkStatusForFlags"); + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) + { + // if target host is not reachable + return NotReachable; + } + + BOOL retVal = NotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) + { + // if target host is reachable and no connection is required + // then we'll assume (for now) that your on Wi-Fi + retVal = ReachableViaWiFi; + } + + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) + { + // ... and the connection is on-demand (or on-traffic) if the + // calling application is using the CFSocketStream or higher APIs + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + // ... and no [user] intervention is needed + retVal = ReachableViaWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + // ... but WWAN connections are OK if the calling application + // is using the CFNetwork (CFSocketStream?) APIs. + retVal = ReachableViaWWAN; + } + return retVal; +} + +- (BOOL) connectionRequired; +{ + NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + return NO; +} + +- (NetworkStatus) currentReachabilityStatus +{ + NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); + NetworkStatus retVal = NotReachable; + SCNetworkReachabilityFlags flags; + netAvailable = NO; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + if(localWiFiRef) + { + retVal = [self localWiFiStatusForFlags: flags]; + } + else + { + retVal = [self networkStatusForFlags: flags]; + } + } + + if(retVal != NotReachable) + netAvailable = YES; + + return retVal; +} +@end