├── Crashlytics.framework ├── Versions │ ├── Current │ └── A │ │ ├── Crashlytics │ │ ├── Resources │ │ └── Info.plist │ │ └── Headers │ │ └── Crashlytics.h ├── Headers ├── Resources ├── Crashlytics ├── run ├── submit └── Modules │ └── module.modulemap ├── Hydro ├── Images.xcassets │ ├── 1.imageset │ │ ├── 1.pdf │ │ └── Contents.json │ ├── 2.imageset │ │ ├── 2.pdf │ │ └── Contents.json │ ├── 3.imageset │ │ ├── 3.pdf │ │ └── Contents.json │ ├── EM.imageset │ │ ├── EM.pdf │ │ └── Contents.json │ ├── HK.imageset │ │ ├── HK.pdf │ │ └── Contents.json │ ├── JP.imageset │ │ ├── JP.pdf │ │ └── Contents.json │ ├── PW.imageset │ │ ├── PW.pdf │ │ └── Contents.json │ ├── logo.imageset │ │ ├── logo.pdf │ │ └── Contents.json │ ├── Check.imageset │ │ ├── Check.pdf │ │ └── Contents.json │ ├── Button.imageset │ │ ├── Button.pdf │ │ └── Contents.json │ ├── flag_sg.imageset │ │ ├── flag_sg.pdf │ │ └── Contents.json │ ├── flag_us.imageset │ │ ├── flag_us.pdf │ │ └── Contents.json │ ├── Map.imageset │ │ ├── World Map color.pdf │ │ └── Contents.json │ ├── World Map.imageset │ │ ├── World Map.pdf │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon-ipad_76.png │ │ ├── icon-iphone_29.png │ │ ├── icon-ipad_76@2x.png │ │ ├── icon-iphone_29@2x.png │ │ ├── icon-iphone_29@3x.png │ │ ├── icon-iphone_40@2x.png │ │ ├── icon-iphone_40@3x.png │ │ ├── icon-iphone_60@2x.png │ │ ├── icon-iphone_60@3x.png │ │ ├── icon-iphone_29@2x-1.png │ │ ├── icon-iphone_40@2x-1.png │ │ ├── icon-iphone_40@3x-1.png │ │ └── Contents.json │ ├── Background.imageset │ │ ├── Background.pdf │ │ └── Contents.json │ ├── Button_wire.imageset │ │ ├── Button_wire.pdf │ │ └── Contents.json │ └── Button + Connect.imageset │ │ ├── Button + Connect.pdf │ │ └── Contents.json ├── Views │ ├── scaleView.h │ ├── locationView.h │ ├── InviteView.h │ ├── stationView.h │ ├── scaleView.m │ ├── stationView.m │ ├── locationView.m │ └── InviteView.m ├── Button │ ├── HydroButton.h │ └── HydroButton.m ├── AppDelegate.h ├── main.m ├── stationDelegate │ ├── VPNStations.h │ └── VPNStations.m ├── Hydro.entitlements ├── ViewController.h ├── GVUserDefaults+Hydro.h ├── VCKetChain.h ├── HydroHelper │ ├── HydroHelper.h │ └── HydroHelper.m ├── GVUserDefaults+Hydro.m ├── LoginViewController │ ├── LoginViewController.h │ └── LoginViewController.m ├── config.json.example ├── VCIPsecVPNManager.h ├── Cell │ ├── StationTableViewCell.h │ └── StationTableViewCell.m ├── Info.plist ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── AppDelegate.m ├── VCKetChain.m ├── VCIPsecVPNManager.m └── ViewController.m ├── README.md ├── Hydro.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Hydro.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── Hydro.xcscmblueprint ├── Podfile ├── Today ├── Today.entitlements ├── VPNButton.h ├── TodayViewController.h ├── Info.plist ├── VPNButton.m ├── TodayViewController.m └── MainInterface.storyboard ├── .gitignore ├── HydroTests ├── Info.plist └── HydroTests.m └── Podfile.lock /Crashlytics.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /Crashlytics.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Crashlytics.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Crashlytics.framework/Crashlytics: -------------------------------------------------------------------------------- 1 | Versions/Current/Crashlytics -------------------------------------------------------------------------------- /Crashlytics.framework/run: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Crashlytics.framework/run -------------------------------------------------------------------------------- /Crashlytics.framework/submit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Crashlytics.framework/submit -------------------------------------------------------------------------------- /Hydro/Images.xcassets/1.imageset/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/1.imageset/1.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/2.imageset/2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/2.imageset/2.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/3.imageset/3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/3.imageset/3.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/EM.imageset/EM.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/EM.imageset/EM.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/HK.imageset/HK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/HK.imageset/HK.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/JP.imageset/JP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/JP.imageset/JP.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/PW.imageset/PW.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/PW.imageset/PW.pdf -------------------------------------------------------------------------------- /Crashlytics.framework/Versions/A/Crashlytics: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Crashlytics.framework/Versions/A/Crashlytics -------------------------------------------------------------------------------- /Hydro/Images.xcassets/logo.imageset/logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/logo.imageset/logo.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hydro 2 | ===== 3 | 4 | [Hydro.network 的开发旅程](http://zhowkev.in/2015/03/09/hydro-network-de-kai-fa-lu-cheng/) 5 | 6 | Hydro Network 7 | -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Check.imageset/Check.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Check.imageset/Check.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button.imageset/Button.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Button.imageset/Button.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/flag_sg.imageset/flag_sg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/flag_sg.imageset/flag_sg.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/flag_us.imageset/flag_us.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/flag_us.imageset/flag_us.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Map.imageset/World Map color.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Map.imageset/World Map color.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/World Map.imageset/World Map.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/World Map.imageset/World Map.pdf -------------------------------------------------------------------------------- /Crashlytics.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Crashlytics { 2 | umbrella header "Crashlytics.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-ipad_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-ipad_76.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Background.imageset/Background.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Background.imageset/Background.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button_wire.imageset/Button_wire.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Button_wire.imageset/Button_wire.pdf -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-ipad_76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-ipad_76@2x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@2x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@3x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@2x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@3x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_60@2x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_60@3x.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_29@2x-1.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@2x-1.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/AppIcon.appiconset/icon-iphone_40@3x-1.png -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button + Connect.imageset/Button + Connect.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatchChat/Hydro.network/HEAD/Hydro/Images.xcassets/Button + Connect.imageset/Button + Connect.pdf -------------------------------------------------------------------------------- /Hydro.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Hydro/Images.xcassets/1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/EM.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "EM.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/HK.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "HK.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/JP.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "JP.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/PW.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "PW.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Button.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Check.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Map.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "World Map color.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/World Map.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "World Map.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/flag_us.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "flag_us.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Background.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button_wire.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Button_wire.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/Button + Connect.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Button + Connect.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hydro/Views/scaleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // scaleView.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface scaleView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Hydro.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Hydro/Button/HydroButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // HydroButton.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HydroButton : UIButton 12 | 13 | -(void)touchDown; 14 | 15 | -(void)touchUpInside; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Hydro/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '6.0' 3 | 4 | target 'Hydro' do 5 | pod 'AFNetworking' 6 | pod 'pop' 7 | pod 'GBPing' 8 | pod 'TPKeyboardAvoiding' 9 | pod 'SAMTextField' 10 | pod 'GVUserDefaults' 11 | pod 'Shimmer' 12 | pod 'SVProgressHUD' 13 | end 14 | 15 | target 'HydroTests' do 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /Hydro/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Hydro/Views/locationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // locationView.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface locationView : UIView 12 | 13 | @property (nonatomic) CAShapeLayer * circleShape; 14 | 15 | @property (nonatomic) CAShapeLayer * ringShape; 16 | 17 | -(void)show; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Hydro/Views/InviteView.h: -------------------------------------------------------------------------------- 1 | // 2 | // InviteView.h 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/27. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface InviteView : UIView 13 | 14 | @property (nonatomic, strong) SAMTextField *emailTextField; 15 | @property (nonatomic, strong) UIButton *inviteButton; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Hydro/Images.xcassets/flag_sg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "flag_sg.pdf" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Hydro/stationDelegate/VPNStations.h: -------------------------------------------------------------------------------- 1 | // 2 | // VPNStations.h 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VPNStations : NSObject 12 | 13 | @property (nonatomic, strong) NSArray *stations; 14 | 15 | @property (nonatomic, strong) NSDictionary *config; 16 | 17 | + (VPNStations *)sharedInstance; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Hydro/Hydro.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.networking.vpn.api 6 | 7 | allow-vpn 8 | 9 | com.apple.security.application-groups 10 | 11 | group.catchlab.TodayExtensionSharingDefaults 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Today/Today.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.networking.vpn.api 6 | 7 | allow-vpn 8 | 9 | com.apple.security.application-groups 10 | 11 | group.catchlab.TodayExtensionSharingDefaults 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Today/VPNButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // VPNButton.h 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, VPNButtonState) { 12 | VPNButtonStateNormal, 13 | VPNButtonStateConnecting, 14 | VPNButtonStateConnected, 15 | VPNButtonStateConnectFailed, 16 | }; 17 | 18 | @interface VPNButton : UIButton 19 | 20 | @property (nonatomic) VPNButtonState buttonState; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Hydro/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HydroHelper.h" 11 | #import "VCIPsecVPNManager.h" 12 | #import "HydroButton.h" 13 | #import "stationView.h" 14 | #import "locationView.h" 15 | 16 | 17 | @interface ViewController : UIViewController 18 | 19 | @property (weak, nonatomic) IBOutlet UITableView *stationsTableVIew; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /Hydro/GVUserDefaults+Hydro.h: -------------------------------------------------------------------------------- 1 | // 2 | // GVUserDefaults+Hydro.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "GVUserDefaults.h" 10 | 11 | @interface GVUserDefaults (Hydro) 12 | 13 | @property (nonatomic, strong) NSString *email; 14 | @property (nonatomic, strong) NSString *server; 15 | @property (nonatomic, strong) NSString *token; 16 | 17 | @property (nonatomic) BOOL ikev2; 18 | 19 | @property (nonatomic, strong) NSDictionary *currentStationDict; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Hydro/VCKetChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCKetChain.h 3 | // VPNCloud 4 | // 5 | // Created by kevinzhow on 14/11/12. 6 | // Copyright (c) 2014年 Kingaxis Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VCKetChain : NSObject 12 | { 13 | NSString * service; 14 | NSString * group; 15 | } 16 | -(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_; 17 | 18 | -(BOOL) insert:(NSString *)key : (NSData *)data; 19 | -(BOOL) update:(NSString*)key :(NSData*) data; 20 | -(BOOL) remove: (NSString*)key; 21 | -(NSData*) find:(NSString*)key; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Hydro/HydroHelper/HydroHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // HydroHelper.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height) 12 | #define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) 13 | 14 | extern NSString * const CCNFillUserInfo; 15 | 16 | extern NSString * const CCNFilterStationStatus; 17 | 18 | 19 | @interface HydroHelper : NSObject 20 | 21 | + (UIImage *)imageFromColor:(UIColor*)color; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Hydro/GVUserDefaults+Hydro.m: -------------------------------------------------------------------------------- 1 | // 2 | // GVUserDefaults+Hydro.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "GVUserDefaults+Hydro.h" 10 | #import "VPNStations.h" 11 | 12 | @implementation GVUserDefaults (Hydro) 13 | 14 | @dynamic email; 15 | @dynamic token; 16 | @dynamic server; 17 | @dynamic ikev2; 18 | @dynamic currentStationDict; 19 | 20 | - (NSDictionary *)setupDefaults { 21 | return @{ 22 | @"server": [[VPNStations sharedInstance].stations.firstObject valueForKey:@"host"] 23 | }; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Hydro/LoginViewController/LoginViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HydroHelper.h" 11 | #import 12 | 13 | @interface LoginViewController : UIViewController 14 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollview; 15 | @property (weak, nonatomic) IBOutlet SAMTextField *emailtextfield; 16 | @property (weak, nonatomic) IBOutlet SAMTextField *passwordField; 17 | 18 | - (IBAction)doClickLoginButton:(id)sender; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | .DS_Store 4 | build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | *.json 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | HockeySDK-iOS/ 28 | Pods/ 29 | # Pods/ 30 | -------------------------------------------------------------------------------- /Hydro/Views/stationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // stationView.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FBShimmeringView.h" 11 | 12 | @interface stationView : UIView 13 | 14 | @property (nonatomic) UIImageView * stationFlag; 15 | 16 | @property (nonatomic) UIImageView * stationStatusIcon; 17 | 18 | @property (nonatomic) UILabel * statusLabel; 19 | 20 | @property (nonatomic) UILabel * nameLabel; 21 | 22 | @property (nonatomic) NSString * name; 23 | 24 | @property (nonatomic) NSString * status; 25 | 26 | @property (nonatomic) FBShimmeringView * shimmeringView; 27 | 28 | @property (nonatomic) BOOL displayed; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /HydroTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | catchlab.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Today/TodayViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TodayViewController.h 3 | // Today 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VPNButton.h" 11 | 12 | @interface TodayViewController : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet VPNButton *country1; 15 | 16 | @property (weak, nonatomic) IBOutlet VPNButton *country2; 17 | @property (weak, nonatomic) IBOutlet VPNButton *country3; 18 | @property (weak, nonatomic) IBOutlet UILabel *signInLabel; 19 | @property (weak, nonatomic) IBOutlet VPNButton *country4; 20 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *countrySpaceConstraint; 21 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *country2SpaceConstraint; 22 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *country3SpaceConstraint; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Hydro/config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "stations": [ 3 | { 4 | "name":"Singapore", 5 | "host":"domain.com", 6 | "short_name":"flag_sg", 7 | "x":0.55, 8 | "y":0.40 9 | }, 10 | { 11 | "name":"HongKong", 12 | "host":"domain.com", 13 | "short_name":"HK", 14 | "x":0.65, 15 | "y":0.20 16 | }, 17 | { 18 | "name":"Japan", 19 | "host":"domain.com", 20 | "short_name":"JP", 21 | "x":0.73, 22 | "y":0.15 23 | }, 24 | { 25 | "name":"United States", 26 | "host":"domain.com", 27 | "short_name":"flag_us", 28 | "x":-0.5, 29 | "y":0.15 30 | } 31 | ], 32 | "server": "http://domain.com", 33 | "server_auth": "/path", 34 | "can_do_invite": "/path", 35 | "do_invite": "/path", 36 | "validate_user": "/path" 37 | } 38 | -------------------------------------------------------------------------------- /Hydro/HydroHelper/HydroHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // HydroHelper.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "HydroHelper.h" 10 | 11 | NSString *const CCNFillUserInfo = @"CCFillUserInfo"; 12 | NSString *const CCNFilterStationStatus = @"CCNFilterStationStatus"; 13 | 14 | @implementation HydroHelper 15 | 16 | + (UIImage *)imageFromColor:(UIColor*)color 17 | { 18 | CGRect rect = CGRectMake(0, 0, 1, 1); 19 | UIGraphicsBeginImageContext(rect.size); 20 | CGContextRef context = UIGraphicsGetCurrentContext(); 21 | CGContextSetFillColorWithColor(context, 22 | [color CGColor]); 23 | // [[UIColor colorWithRed:222./255 green:227./255 blue: 229./255 alpha:1] CGColor]) ; 24 | CGContextFillRect(context, rect); 25 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 26 | UIGraphicsEndImageContext(); 27 | return img; 28 | } 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Crashlytics.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | Crashlytics 9 | CFBundleIdentifier 10 | com.crashlytics.ios 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Crashlytics 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.2.10 19 | CFBundleSupportedPlatforms 20 | 21 | iPhoneOS 22 | 23 | CFBundleVersion 24 | 45 25 | DTPlatformName 26 | iphoneos 27 | MinimumOSVersion 28 | 4.0 29 | 30 | 31 | -------------------------------------------------------------------------------- /HydroTests/HydroTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HydroTests.m 3 | // HydroTests 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HydroTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation HydroTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Today/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Hydro 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | catchlab.Hydro.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.1.5 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 12 25 | NSExtension 26 | 27 | NSExtensionMainStoryboard 28 | MainInterface 29 | NSExtensionPointIdentifier 30 | com.apple.widget-extension 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Hydro/VCIPsecVPNManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCIPsecVPNManager.h 3 | // VPNCloud 4 | // 5 | // Created by kevinzhow on 14/11/12. 6 | // Copyright (c) 2014年 Kingaxis Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | @interface VCIPsecVPNManager : NSObject 14 | 15 | @property (nonatomic) NEVPNManager * vpnManager; 16 | 17 | -(void)prepareWithCompletion:(void (^)(NSError *error))done; 18 | 19 | -(void)connectIPSecIKEv2WithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andP12Name:(NSString *)p12Name andidentityDataPassword:(NSString *)identityDataPassword andGroupName:(NSString *)groupName; 20 | 21 | -(void)connectIPSecWithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andPSK:(NSString *)psk andGroupName:(NSString *)groupName; 22 | 23 | -(void)connectIPSecIKEv2WithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andPSK:(NSString *)psk andGroupName:(NSString *)groupName; 24 | 25 | @property (nonatomic) BOOL IKEv1Enabled; 26 | 27 | @property (nonatomic) BOOL IKEv2Enabled; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Hydro/Cell/StationTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // StationTableViewCell.h 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | @interface StationTableViewCell : UITableViewCell 14 | 15 | @property (weak, nonatomic) IBOutlet UIImageView *statusIcon; 16 | 17 | @property (nonatomic) NSString * domain; 18 | 19 | @property (weak, nonatomic) IBOutlet UIImageView *flagImageView; 20 | 21 | @property (weak, nonatomic) IBOutlet UILabel *stationNameLabel; 22 | 23 | @property (nonatomic) BOOL isPing; 24 | 25 | @property (nonatomic) int stopPingCount; 26 | 27 | @property (nonatomic) NSMutableArray * allPingResult; 28 | 29 | @property (nonatomic) GBPing * ping; 30 | 31 | @property (nonatomic) float rtt; 32 | 33 | @property (weak, nonatomic) IBOutlet UIImageView *checkStatusIcon; 34 | 35 | @property (nonatomic) NSDictionary * stationDic; 36 | 37 | -(void)makeCheck; 38 | 39 | @property (nonatomic) BOOL checked; 40 | 41 | -(void)setDomain:(NSString *)domain withIndex:(NSInteger)index; 42 | @property (nonatomic) NSInteger failCount; 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Hydro/stationDelegate/VPNStations.m: -------------------------------------------------------------------------------- 1 | // 2 | // VPNStations.m 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "VPNStations.h" 10 | 11 | @interface VPNStations() 12 | 13 | @end 14 | 15 | @implementation VPNStations 16 | 17 | + (VPNStations *)sharedInstance 18 | { 19 | static VPNStations *_sharedInstance = nil; 20 | 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | _sharedInstance = [[VPNStations alloc] init]; 24 | }); 25 | 26 | return _sharedInstance; 27 | } 28 | 29 | - (id)init{ 30 | self = [super init]; 31 | if (self) { 32 | 33 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"json"]; 34 | NSData *JSONData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:nil]; 35 | NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableContainers error:nil]; 36 | self.stations = [jsonObject valueForKey:@"stations"]; 37 | self.config = jsonObject; 38 | 39 | } 40 | return self; 41 | } 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Hydro/Button/HydroButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // HydroButton.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "HydroButton.h" 10 | #import 11 | 12 | @implementation HydroButton 13 | 14 | /* 15 | // Only override drawRect: if you perform custom drawing. 16 | // An empty implementation adversely affects performance during animation. 17 | - (void)drawRect:(CGRect)rect { 18 | // Drawing code 19 | } 20 | */ 21 | 22 | 23 | - (void)touchDown { 24 | [self.layer pop_removeAnimationForKey:@"AnimationScaleBack"]; 25 | POPSpringAnimation *anim = 26 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 27 | anim.springBounciness = 10; 28 | anim.springSpeed = 20; 29 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.8, 0.8)]; 30 | [self.layer pop_addAnimation:anim forKey:@"AnimationScale"]; 31 | } 32 | 33 | 34 | 35 | - (void)touchUpInside{ 36 | 37 | [self.layer pop_removeAnimationForKey:@"AnimationScale"]; 38 | POPSpringAnimation *anim = 39 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 40 | anim.springBounciness = 10; 41 | anim.springSpeed = 20; 42 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 43 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 44 | 45 | 46 | } 47 | 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Hydro/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | catchlab.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.1.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 12 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UIStatusBarStyle 36 | UIStatusBarStyleLightContent 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | 41 | UIViewControllerBasedStatusBarAppearance 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.5.4): 3 | - AFNetworking/NSURLConnection (= 2.5.4) 4 | - AFNetworking/NSURLSession (= 2.5.4) 5 | - AFNetworking/Reachability (= 2.5.4) 6 | - AFNetworking/Security (= 2.5.4) 7 | - AFNetworking/Serialization (= 2.5.4) 8 | - AFNetworking/UIKit (= 2.5.4) 9 | - AFNetworking/NSURLConnection (2.5.4): 10 | - AFNetworking/Reachability 11 | - AFNetworking/Security 12 | - AFNetworking/Serialization 13 | - AFNetworking/NSURLSession (2.5.4): 14 | - AFNetworking/Reachability 15 | - AFNetworking/Security 16 | - AFNetworking/Serialization 17 | - AFNetworking/Reachability (2.5.4) 18 | - AFNetworking/Security (2.5.4) 19 | - AFNetworking/Serialization (2.5.4) 20 | - AFNetworking/UIKit (2.5.4): 21 | - AFNetworking/NSURLConnection 22 | - AFNetworking/NSURLSession 23 | - GBPing (1.1.1) 24 | - GVUserDefaults (1.0.1) 25 | - pop (1.0.7) 26 | - SAMTextField (0.1.2) 27 | - Shimmer (1.0.2) 28 | - SVProgressHUD (1.1.3) 29 | - TPKeyboardAvoiding (1.2.6) 30 | 31 | DEPENDENCIES: 32 | - AFNetworking 33 | - GBPing 34 | - GVUserDefaults 35 | - pop 36 | - SAMTextField 37 | - Shimmer 38 | - SVProgressHUD 39 | - TPKeyboardAvoiding 40 | 41 | SPEC CHECKSUMS: 42 | AFNetworking: 05edc0ac4c4c8cf57bcf4b84be5b0744b6d8e71e 43 | GBPing: 96434766235731ff5b4573584fac8add16b31f71 44 | GVUserDefaults: acb40729f70984008d0650e49d7e422f1bbfa597 45 | pop: 628ffc631644601567ee8bfaaaea493ebd7d0923 46 | SAMTextField: a0f4eb5b48b45aef56453c265262b45154bc3e2a 47 | Shimmer: c5374be1c2b0c9e292fb05b339a513cf291cac86 48 | SVProgressHUD: 748080e4f36e603f6c02aec292664239df5279c1 49 | TPKeyboardAvoiding: ee4dd35e3bb7bb64bfbda47e7cc9ba872ff10f77 50 | 51 | COCOAPODS: 0.38.2 52 | -------------------------------------------------------------------------------- /Hydro.xcworkspace/xcshareddata/Hydro.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "AD9AE3CADFCF220BDF608F8FE2C3794930E9FBE8", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "66ECCB7E69137BF099D26851106CA4CBC27A9471" : 0, 8 | "AD9AE3CADFCF220BDF608F8FE2C3794930E9FBE8" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "CFBCE743-3F8E-41D8-8437-8AABAB64B189", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "66ECCB7E69137BF099D26851106CA4CBC27A9471" : "Hydro", 13 | "AD9AE3CADFCF220BDF608F8FE2C3794930E9FBE8" : "Hydro_Opensource" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Hydro", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 203, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Hydro.xcworkspace", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/CatchChat\/Hydro.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "66ECCB7E69137BF099D26851106CA4CBC27A9471" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:CatchChat\/Hydro.network.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "AD9AE3CADFCF220BDF608F8FE2C3794930E9FBE8" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /Hydro/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon-iphone_29@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon-iphone_29@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "icon-iphone_40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "icon-iphone_40@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "icon-iphone_60@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon-iphone_60@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "icon-iphone_29.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon-iphone_29@2x-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "icon-iphone_40@2x-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon-iphone_40@3x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "icon-ipad_76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "icon-ipad_76@2x.png", 73 | "scale" : "2x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | } 80 | } -------------------------------------------------------------------------------- /Hydro/Views/scaleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // scaleView.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "scaleView.h" 10 | #import 11 | 12 | @implementation scaleView 13 | 14 | /* 15 | // Only override drawRect: if you perform custom drawing. 16 | // An empty implementation adversely affects performance during animation. 17 | - (void)drawRect:(CGRect)rect { 18 | // Drawing code 19 | } 20 | */ 21 | 22 | 23 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 24 | [self.layer pop_removeAnimationForKey:@"AnimationScaleBack"]; 25 | POPSpringAnimation *anim = 26 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 27 | anim.springBounciness = 10; 28 | anim.springSpeed = 20; 29 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 30 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.8, 0.8)]; 31 | [self.layer pop_addAnimation:anim forKey:@"AnimationScale"]; 32 | } 33 | 34 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 35 | [self.layer pop_removeAnimationForKey:@"AnimationScale"]; 36 | POPSpringAnimation *anim = 37 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 38 | anim.springBounciness = 10; 39 | anim.springSpeed = 20; 40 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0.8, 0.8)]; 41 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 42 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 43 | } 44 | 45 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 46 | 47 | [self.layer pop_removeAnimationForKey:@"AnimationScale"]; 48 | POPSpringAnimation *anim = 49 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 50 | anim.springBounciness = 10; 51 | anim.springSpeed = 20; 52 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0.8, 0.8)]; 53 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 54 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 55 | 56 | 57 | 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Hydro/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Hydro/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | [Crashlytics startWithAPIKey:@"de004490005a062fa95a4d5676a7edbfbe42c582"]; 22 | application.applicationSupportsShakeToEdit = YES; 23 | 24 | return YES; 25 | } 26 | 27 | - (void)applicationWillResignActive:(UIApplication *)application { 28 | // 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. 29 | // 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. 30 | } 31 | 32 | - (void)applicationDidEnterBackground:(UIApplication *)application { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Today/VPNButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // VPNButton.m 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "VPNButton.h" 10 | #import "HydroHelper.h" 11 | 12 | @interface VPNButton() 13 | 14 | @property (nonatomic, strong) CAShapeLayer *maskShapeLayer; 15 | @property (nonatomic, strong) CAShapeLayer *stateShapeLayer; 16 | 17 | @end 18 | 19 | @implementation VPNButton 20 | 21 | - (void)awakeFromNib 22 | { 23 | [super awakeFromNib]; 24 | 25 | self.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.12]; 26 | 27 | [self setBackgroundImage:[HydroHelper imageFromColor:[UIColor colorWithWhite:0.0 alpha:0.32]] forState:UIControlStateHighlighted]; 28 | } 29 | 30 | - (CAShapeLayer *)maskShapeLayer 31 | { 32 | if (!_maskShapeLayer) { 33 | _maskShapeLayer = [CAShapeLayer layer]; 34 | 35 | self.layer.mask = _maskShapeLayer; 36 | } 37 | 38 | return _maskShapeLayer; 39 | } 40 | 41 | - (CAShapeLayer *)stateShapeLayer 42 | { 43 | if (!_stateShapeLayer) { 44 | _stateShapeLayer = [CAShapeLayer layer]; 45 | _stateShapeLayer.lineWidth = 3.0; 46 | _stateShapeLayer.strokeColor = [UIColor clearColor].CGColor; 47 | _stateShapeLayer.fillColor = [UIColor clearColor].CGColor; 48 | _stateShapeLayer.lineJoin = @"round"; 49 | _stateShapeLayer.lineCap = @"round"; 50 | 51 | [self.layer addSublayer:_stateShapeLayer]; 52 | } 53 | 54 | return _stateShapeLayer; 55 | } 56 | 57 | - (UIBezierPath *)polygonPathWithRect:(CGRect)rect slides:(NSInteger)slides rotationAngle:(CGFloat)rotationAngle scale:(CGFloat)scale; 58 | { 59 | CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5 * scale; 60 | CGFloat stepAngle = M_PI * 2.0 / slides; 61 | CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5); 62 | 63 | UIBezierPath *path = [UIBezierPath bezierPath]; 64 | [path moveToPoint:CGPointMake(center.x + radius * cosf(0 + stepAngle * 0.5), center.y + radius * sinf(0 + stepAngle * 0.5))]; 65 | 66 | for (NSInteger i = 1; i < 6; i++) { 67 | CGFloat angle = stepAngle * (i); 68 | [path addLineToPoint:CGPointMake(center.x + radius * cosf(angle + stepAngle * 0.5), center.y + radius * sinf(angle + stepAngle * 0.5))]; 69 | } 70 | 71 | [path closePath]; 72 | 73 | return path; 74 | } 75 | 76 | - (void)layoutSubviews 77 | { 78 | [super layoutSubviews]; 79 | 80 | UIBezierPath *maskPath = [self polygonPathWithRect:self.bounds slides:6 rotationAngle:M_PI / 6.0 scale:1.0]; 81 | self.maskShapeLayer.path = maskPath.CGPath; 82 | 83 | UIBezierPath *statePath = [self polygonPathWithRect:self.bounds slides:6 rotationAngle:M_PI / 6.0 scale:0.9]; 84 | self.stateShapeLayer.path = statePath.CGPath; 85 | } 86 | 87 | - (void)setButtonState:(VPNButtonState)buttonState 88 | { 89 | _buttonState = buttonState; 90 | 91 | NSArray *colors = @[ 92 | [UIColor clearColor], 93 | [UIColor yellowColor], 94 | [UIColor colorWithRed:60/255.0 green:171/255.0 blue:218/255.0 alpha:1.0], 95 | [UIColor lightGrayColor], 96 | ]; 97 | 98 | UIColor *color = colors[_buttonState % colors.count]; 99 | self.stateShapeLayer.strokeColor = color.CGColor; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Hydro/Views/stationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // stationView.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "stationView.h" 10 | #import "HydroHelper.h" 11 | 12 | 13 | @implementation stationView 14 | 15 | 16 | -(id)initWithFrame:(CGRect)frame 17 | { 18 | self = [super initWithFrame:frame]; 19 | 20 | if (self) { 21 | 22 | float flagWidth = self.frame.size.width /5; 23 | self.stationFlag = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width/2.0 - flagWidth/2.0, 30, flagWidth, self.frame.size.height / 2)]; 24 | [self addSubview:self.stationFlag]; 25 | self.stationFlag.contentMode = UIViewContentModeScaleAspectFit; 26 | 27 | self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.stationFlag.frame.size.height + 15.0, frame.size.width, 35.0)]; 28 | self.nameLabel.textColor = [UIColor whiteColor]; 29 | self.nameLabel.font = [UIFont fontWithName:@"Avenir-Medium" size:28.0]; 30 | self.nameLabel.textAlignment = NSTextAlignmentCenter; 31 | [self addSubview:self.nameLabel]; 32 | 33 | self.statusLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 34 | self.statusLabel.textColor = [UIColor whiteColor]; 35 | self.statusLabel.font = [UIFont fontWithName:@"Avenir" size:16.0]; 36 | self.statusLabel.textAlignment = NSTextAlignmentCenter; 37 | [self addSubview:self.statusLabel]; 38 | 39 | self.stationStatusIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 40 | 41 | [self addSubview:self.stationStatusIcon]; 42 | 43 | 44 | self.shimmeringView = [[FBShimmeringView alloc] initWithFrame:self.statusLabel.bounds]; 45 | [self addSubview:self.shimmeringView]; 46 | 47 | self.shimmeringView.shimmeringSpeed = 80.0; 48 | 49 | self.shimmeringView.contentView = self.statusLabel; 50 | 51 | _status = @"Disconnected"; 52 | 53 | 54 | } 55 | 56 | return self; 57 | } 58 | 59 | 60 | -(void)setName:(NSString *)name 61 | { 62 | _name = name; 63 | self.nameLabel.text = name; 64 | } 65 | 66 | 67 | -(void)setStatus:(NSString *)status 68 | { 69 | _status = status; 70 | self.statusLabel.text = status; 71 | [self.statusLabel sizeToFit]; 72 | 73 | self.shimmeringView.frame = self.statusLabel.frame; 74 | 75 | if ([status isEqualToString:@"Connected"]) { 76 | // Start shimmering. 77 | self.shimmeringView.shimmering = NO; 78 | self.shimmeringView.center = CGPointMake(SCREEN_WIDTH/2.0 + 5.0, self.nameLabel.center.y + self.nameLabel.frame.size.height / 2 + 15.0); 79 | self.stationStatusIcon.image = [UIImage imageNamed:@"1"]; 80 | }else{ 81 | // Start shimmering. 82 | self.shimmeringView.shimmering = YES; 83 | self.stationStatusIcon.image = nil; 84 | self.shimmeringView.center = CGPointMake(SCREEN_WIDTH/2.0, self.nameLabel.center.y + self.nameLabel.frame.size.height / 2 + 15.0); 85 | } 86 | 87 | self.stationStatusIcon.center = CGPointMake(self.shimmeringView.center.x - self.shimmeringView.frame.size.width /2.0 - 10.0, self.nameLabel.center.y + self.nameLabel.frame.size.height / 2 + 15.0); 88 | 89 | 90 | } 91 | /* 92 | // Only override drawRect: if you perform custom drawing. 93 | // An empty implementation adversely affects performance during animation. 94 | - (void)drawRect:(CGRect)rect { 95 | // Drawing code 96 | } 97 | */ 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Hydro/VCKetChain.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCKetChain.m 3 | // VPNCloud 4 | // 5 | // Created by kevinzhow on 14/11/12. 6 | // Copyright (c) 2014年 Kingaxis Inc. All rights reserved. 7 | // 8 | 9 | #import "VCKetChain.h" 10 | #import 11 | 12 | @implementation VCKetChain 13 | -(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_ 14 | { 15 | self =[super init]; 16 | if(self) 17 | { 18 | service = [NSString stringWithString:service_]; 19 | 20 | if(group_) 21 | group = [NSString stringWithString:group_]; 22 | } 23 | 24 | return self; 25 | } 26 | -(NSMutableDictionary*) prepareDict:(NSString *) key 27 | { 28 | 29 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 30 | [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 31 | 32 | NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding]; 33 | [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric]; 34 | [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount]; 35 | [dict setObject:service forKey:(__bridge id)kSecAttrService]; 36 | [dict setObject:@YES forKey:(__bridge id)kSecReturnPersistentRef]; 37 | [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible]; 38 | 39 | //This is for sharing data across apps 40 | if(group != nil) 41 | [dict setObject:group forKey:(__bridge id)kSecAttrAccessGroup]; 42 | 43 | return dict; 44 | 45 | } 46 | -(BOOL) insert:(NSString *)key : (NSData *)data 47 | { 48 | NSMutableDictionary * dict =[self prepareDict:key]; 49 | [dict setObject:data forKey:(__bridge id)kSecValueData]; 50 | 51 | OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL); 52 | if(errSecSuccess != status) { 53 | NSLog(@"Unable add item with key =%@ error:%ld",key,status); 54 | } 55 | return (errSecSuccess == status); 56 | } 57 | 58 | 59 | static NSString * const serviceName = @"im.zorro.ipsec_demo.vpn_config"; 60 | -(NSData*) find:(NSString*)key 61 | { 62 | 63 | // NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init]; 64 | // 65 | // NSData *encodedIdentifier = [key dataUsingEncoding:NSUTF8StringEncoding]; 66 | // 67 | // searchDictionary[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; 68 | // searchDictionary[(__bridge id)kSecAttrGeneric] = encodedIdentifier; 69 | // searchDictionary[(__bridge id)kSecAttrAccount] = encodedIdentifier; 70 | // searchDictionary[(__bridge id)kSecAttrService] = serviceName; 71 | // 72 | // searchDictionary[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; 73 | // searchDictionary[(__bridge id)kSecReturnPersistentRef] = @YES; 74 | // 75 | // CFTypeRef result = NULL; 76 | // SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &result); 77 | // 78 | // return (__bridge_transfer NSData *)result; 79 | 80 | 81 | NSMutableDictionary *dict = [self prepareDict:key]; 82 | [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 83 | // [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 84 | CFTypeRef result = NULL; 85 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result); 86 | 87 | if( status != errSecSuccess) { 88 | NSLog(@"Unable to fetch item for key %@ with error:%ld",key,status); 89 | return nil; 90 | } 91 | 92 | return (__bridge NSData *)result; 93 | } 94 | 95 | 96 | 97 | -(BOOL) update:(NSString*)key :(NSData*) data 98 | { 99 | NSMutableDictionary * dictKey =[self prepareDict:key]; 100 | 101 | NSMutableDictionary * dictUpdate =[[NSMutableDictionary alloc] init]; 102 | [dictUpdate setObject:data forKey:(__bridge id)kSecValueData]; 103 | 104 | OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)dictKey, (__bridge CFDictionaryRef)dictUpdate); 105 | if(errSecSuccess != status) { 106 | NSLog(@"Unable add update with key =%@ error:%ld",key,status); 107 | } 108 | return (errSecSuccess == status); 109 | 110 | return YES; 111 | 112 | } 113 | -(BOOL) remove: (NSString*)key 114 | { 115 | NSMutableDictionary *dict = [self prepareDict:key]; 116 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict); 117 | if( status != errSecSuccess) { 118 | NSLog(@"Unable to remove item for key %@ with error:%ld",key,status); 119 | return NO; 120 | } 121 | return YES; 122 | } 123 | @end 124 | -------------------------------------------------------------------------------- /Hydro/Views/locationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // locationView.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "locationView.h" 10 | #import 11 | 12 | @implementation locationView 13 | 14 | /* 15 | // Only override drawRect: if you perform custom drawing. 16 | // An empty implementation adversely affects performance during animation. 17 | - (void)drawRect:(CGRect)rect { 18 | // Drawing code 19 | } 20 | */ 21 | 22 | -(id)initWithFrame:(CGRect)frame{ 23 | self = [super initWithFrame:frame]; 24 | if (self) { 25 | self.backgroundColor = [UIColor clearColor]; 26 | int radius = frame.size.width/6; 27 | int radius_2 = frame.size.width/6.0; 28 | 29 | self.circleShape = [CAShapeLayer layer]; 30 | self.circleShape.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) 31 | cornerRadius:radius].CGPath;; 32 | self.circleShape.strokeColor = [[UIColor whiteColor] CGColor]; 33 | self.circleShape.fillColor = [UIColor whiteColor].CGColor; 34 | self.circleShape.lineWidth = 1.0; 35 | 36 | self.circleShape.position = CGPointMake(self.frame.size.width/2.0-radius, 37 | self.frame.size.height/2.0-radius); 38 | 39 | self.ringShape = [CAShapeLayer layer]; 40 | self.ringShape.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius_2, 2.0*radius_2) 41 | cornerRadius:radius_2].CGPath;; 42 | self.ringShape.strokeColor = [[UIColor whiteColor] CGColor]; 43 | self.ringShape.fillColor = nil; 44 | self.ringShape.lineWidth = 1.0; 45 | 46 | 47 | self.ringShape.position = CGPointMake(self.frame.size.width/2.0-radius_2, 48 | self.frame.size.height/2.0-radius_2); 49 | 50 | // Add CAShapeLayer to our view 51 | 52 | [self.layer addSublayer:self.circleShape]; 53 | [self.layer addSublayer:self.ringShape]; 54 | 55 | [self waveAnimation]; 56 | } 57 | 58 | return self; 59 | } 60 | 61 | 62 | -(void)waveAnimation 63 | { 64 | int radius = self.frame.size.width/6; 65 | int radius_2 = self.frame.size.width/6.0; 66 | 67 | POPBasicAnimation *animAlpha = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 68 | animAlpha.fromValue = @1.0; 69 | animAlpha.toValue = @0.0; 70 | animAlpha.duration = 3.0; 71 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 72 | 73 | if (finished) { 74 | [self waveAnimation]; 75 | }}; 76 | [self.ringShape pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 77 | 78 | 79 | 80 | CABasicAnimation * pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 81 | pathAnimation.fromValue = (id)[UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius_2, self.frame.size.height/2.0-radius_2, 2.0*radius_2, 2.0*radius_2) 82 | cornerRadius:radius_2].CGPath;; 83 | pathAnimation.toValue = (id)[UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius_2*8, self.frame.size.height/2.0-radius_2*8, 8.0*2*radius_2, 8.0*2*radius_2) 84 | cornerRadius:radius_2*8].CGPath;; 85 | pathAnimation.duration = 3.0f; 86 | pathAnimation.autoreverses = NO; 87 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 88 | [self.ringShape addAnimation:pathAnimation forKey:@"animationKey"]; 89 | 90 | self.ringShape.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius_2*8, self.frame.size.height/2.0-radius_2*8, 8.0*2*radius_2, 8.0*2*radius_2) 91 | cornerRadius:radius_2*8].CGPath;; 92 | 93 | 94 | 95 | CABasicAnimation * path2Animation = [CABasicAnimation animationWithKeyPath:@"path"]; 96 | path2Animation.fromValue = (id)[UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius, self.frame.size.height/2.0-radius, 2.0*radius, 2.0*radius) 97 | cornerRadius:radius].CGPath;; 98 | path2Animation.toValue = (id)[UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius, self.frame.size.height/2.0-radius, 2.0*radius, 2.0*radius) 99 | cornerRadius:radius].CGPath;; 100 | path2Animation.duration = 3.0f; 101 | path2Animation.autoreverses = NO; 102 | path2Animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 103 | [self.circleShape addAnimation:path2Animation forKey:@"animationKey"]; 104 | 105 | self.circleShape.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.frame.size.width/2.0-radius, self.frame.size.height/2.0-radius, 2.0*radius, 2.0*radius) 106 | cornerRadius:radius].CGPath;; 107 | } 108 | 109 | 110 | -(void)show 111 | { 112 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 113 | animAlpha.toValue = @1.0; 114 | animAlpha.springBounciness = 0.5; 115 | animAlpha.springSpeed = 12.0; 116 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 117 | if (finished) {}}; 118 | [self.layer pop_addAnimation:animAlpha forKey:@"AlphaLocation"]; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /Hydro/LoginViewController/LoginViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "LoginViewController.h" 10 | #import 11 | #import "GVUserDefaults+Hydro.h" 12 | #import 13 | #import "VPNStations.h" 14 | 15 | @interface LoginViewController () 16 | 17 | @end 18 | 19 | @implementation LoginViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | UITapGestureRecognizer *letterTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(highlightLetter:)]; 24 | [self.view addGestureRecognizer:letterTapRecognizer]; 25 | self.view.backgroundColor = [UIColor clearColor]; 26 | 27 | 28 | self.emailtextfield.delegate = self; 29 | self.passwordField.delegate = self; 30 | self.passwordField.adjustsFontSizeToFitWidth = YES; 31 | self.passwordField.minimumFontSize = 12; 32 | 33 | self.emailtextfield.adjustsFontSizeToFitWidth = YES; 34 | self.emailtextfield.minimumFontSize = 12; 35 | 36 | self.passwordField.textEdgeInsets = UIEdgeInsetsMake(0.0f, 40.0f, 10.0f, 10.0f); 37 | 38 | UIFont *font = [UIFont fontWithName:@"Avenir-Medium" size:17.0]; 39 | NSDictionary *attrsDictionary = 40 | [NSDictionary dictionaryWithObjectsAndKeys:font,NSFontAttributeName, 41 | [UIColor colorWithWhite:1.0 alpha:0.5], NSForegroundColorAttributeName, 42 | nil]; 43 | 44 | NSAttributedString *attrString = 45 | [[NSAttributedString alloc] initWithString:@"Password" 46 | attributes:attrsDictionary]; 47 | 48 | NSAttributedString *attrEmailString = 49 | [[NSAttributedString alloc] initWithString:@"Email" 50 | attributes:attrsDictionary]; 51 | 52 | self.passwordField.attributedPlaceholder = attrString; 53 | 54 | self.emailtextfield.attributedPlaceholder = attrEmailString; 55 | 56 | self.emailtextfield.textEdgeInsets = UIEdgeInsetsMake(0.0f, 40.0f, 10.0f, 10.0f); 57 | 58 | // Do any additional setup after loading the view. 59 | } 60 | 61 | 62 | - (BOOL) textFieldShouldReturn:(UITextField *)textField 63 | { 64 | if (textField == self.emailtextfield) 65 | { 66 | [self.emailtextfield resignFirstResponder]; 67 | [self.passwordField becomeFirstResponder]; 68 | } 69 | else if (textField == self.passwordField) 70 | { 71 | [self.passwordField resignFirstResponder]; 72 | 73 | [self doClickLoginButton:self]; 74 | } 75 | 76 | return true; 77 | } 78 | 79 | 80 | -(IBAction) highlightLetter:(UITapGestureRecognizer*)recognizer 81 | { 82 | // UIView *view = [recognizer view]; 83 | [self.view endEditing:YES]; 84 | } 85 | 86 | 87 | - (void)didReceiveMemoryWarning { 88 | [super didReceiveMemoryWarning]; 89 | // Dispose of any resources that can be recreated. 90 | } 91 | 92 | /* 93 | #pragma mark - Navigation 94 | 95 | // In a storyboard-based application, you will often want to do a little preparation before navigation 96 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 97 | // Get the new view controller using [segue destinationViewController]. 98 | // Pass the selected object to the new view controller. 99 | } 100 | */ 101 | 102 | - (IBAction)doClickLoginButton:(id)sender { 103 | 104 | if (self.emailtextfield.text.length < 1 || self.passwordField.text.length < 1) { 105 | 106 | 107 | return; 108 | 109 | } 110 | 111 | [SVProgressHUD show]; 112 | 113 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 114 | NSDictionary *parameters = @{ 115 | @"email": self.emailtextfield.text, 116 | @"password":self.passwordField.text 117 | }; 118 | [manager POST:[NSString stringWithFormat:@"%@%@",[[VPNStations sharedInstance].config valueForKey:@"server"], [[VPNStations sharedInstance].config valueForKey:@"server_auth"]] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { 119 | NSLog(@"JSON: %@", responseObject); 120 | 121 | 122 | NSString * token = [responseObject valueForKey:@"message"]; 123 | NSString * status = [responseObject valueForKey:@"status"]; 124 | if([status isEqualToString:@"error"]){ 125 | 126 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 127 | // time-consuming task 128 | dispatch_async(dispatch_get_main_queue(), ^{ 129 | [SVProgressHUD showErrorWithStatus:@"Auth Error"]; 130 | }); 131 | }); 132 | }else{ 133 | 134 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 135 | // time-consuming task 136 | dispatch_async(dispatch_get_main_queue(), ^{ 137 | [SVProgressHUD dismiss]; 138 | }); 139 | }); 140 | [GVUserDefaults standardUserDefaults].token = token; 141 | 142 | 143 | 144 | [[NSNotificationCenter defaultCenter] postNotificationName:CCNFillUserInfo object:nil]; 145 | [self dismissViewControllerAnimated:YES completion:^{ 146 | 147 | }]; 148 | 149 | NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 150 | 151 | [sharedDefaults setBool:YES forKey:@"ActiveToday"]; 152 | [sharedDefaults synchronize]; // (!!) This is crucial. 153 | } 154 | 155 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 156 | NSLog(@"Error: %@", error); 157 | 158 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 159 | // time-consuming task 160 | dispatch_async(dispatch_get_main_queue(), ^{ 161 | [SVProgressHUD dismiss]; 162 | }); 163 | }); 164 | }]; 165 | 166 | 167 | } 168 | @end 169 | -------------------------------------------------------------------------------- /Hydro/Cell/StationTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // StationTableViewCell.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "StationTableViewCell.h" 10 | #import 11 | #import "HydroHelper.h" 12 | 13 | @implementation StationTableViewCell 14 | 15 | - (void)awakeFromNib { 16 | // Initialization code 17 | [[NSNotificationCenter defaultCenter] addObserver:self 18 | selector:@selector(filterStationStatus:) 19 | name:CCNFilterStationStatus 20 | object:nil]; 21 | self.backgroundColor = [UIColor clearColor]; 22 | } 23 | 24 | 25 | -(void)filterStationStatus:(NSNotification *)notification 26 | { 27 | NSDictionary * dict = notification.object; 28 | if (![[dict valueForKey:@"host"] isEqualToString:self.domain]) { 29 | [self uncheck]; 30 | }else{ 31 | if (!self.checked) { 32 | [self doCheck]; 33 | } 34 | } 35 | } 36 | 37 | -(void)ping:(GBPing *)pinger didReceiveReplyWithSummary:(GBPingSummary *)summary { 38 | 39 | // NSLog(@"REPLY %@ > %@", pinger.host, summary); 40 | [self checkFinalStatusWith:pinger andSummert:summary]; 41 | 42 | } 43 | 44 | -(void)checkFinalStatusWith:(GBPing *)pinger andSummert:(GBPingSummary *)summary 45 | { 46 | self.rtt += summary.rtt; 47 | if(self.stopPingCount < 5){ 48 | self.stopPingCount += 1; 49 | [self.allPingResult addObject:summary]; 50 | 51 | }else{ 52 | self.isPing = NO; 53 | [self.ping stop]; 54 | float status = self.rtt/self.stopPingCount * 1000; 55 | // NSLog(@"Result is %.3f", status); 56 | 57 | if (status < 150) { 58 | self.statusIcon.image = [UIImage imageNamed:@"1"]; 59 | }else if (status >= 150 && status < 250){ 60 | self.statusIcon.image = [UIImage imageNamed:@"2"]; 61 | }else{ 62 | self.statusIcon.image = [UIImage imageNamed:@"3"]; 63 | } 64 | 65 | [self updateFailedCount]; 66 | 67 | } 68 | } 69 | 70 | -(void)updateFailedCount 71 | { 72 | if (self.failCount / 5.0 > 0.2) { 73 | self.statusIcon.image = [UIImage imageNamed:@"3"]; 74 | } 75 | } 76 | 77 | -(void)setDomain:(NSString *)domain withIndex:(NSInteger)index 78 | { 79 | _domain = domain; 80 | 81 | 82 | self.ping = [[GBPing alloc] init]; 83 | self.ping.host = domain; 84 | self.ping.delegate = self; 85 | self.ping.timeout = 4; 86 | self.ping.pingPeriod = 0.3 + (0.1 * index); 87 | self.stopPingCount = 0; 88 | self.isPing = YES; 89 | self.allPingResult = [NSMutableArray new]; 90 | self.failCount = 0; 91 | 92 | [self.ping setupWithBlock:^(BOOL success, NSError *error) { //necessary to resolve hostname 93 | if (success) { 94 | //start pinging 95 | [self.ping startPinging]; 96 | } 97 | else { 98 | NSLog(@"failed to start"); 99 | } 100 | }]; 101 | 102 | } 103 | 104 | //-(void)ping:(GBPing *)pinger didReceiveUnexpectedReplyWithSummary:(GBPingSummary *)summary { 105 | // NSLog(@"BREPLY> %@", summary); 106 | // self.failCount += 1; 107 | // self.stopPingCount += 1; 108 | // [self checkFinalStatusWith:pinger andSummert:summary]; 109 | //} 110 | 111 | //-(void)ping:(GBPing *)pinger didSendPingWithSummary:(GBPingSummary *)summary { 112 | // NSLog(@"SENT %@ > %@", pinger.host ,summary); 113 | //} 114 | 115 | -(void)ping:(GBPing *)pinger didTimeoutWithSummary:(GBPingSummary *)summary { 116 | NSLog(@"TIMOUT> %@", summary); 117 | self.failCount += 1; 118 | self.stopPingCount += 1; 119 | [self checkFinalStatusWith:pinger andSummert:summary]; 120 | } 121 | 122 | -(void)ping:(GBPing *)pinger didFailWithError:(NSError *)error { 123 | NSLog(@"FAIL> %@", error); 124 | self.failCount += 1; 125 | self.stopPingCount += 1; 126 | [self updateFailedCount]; 127 | } 128 | // 129 | -(void)ping:(GBPing *)pinger didFailToSendPingWithSummary:(GBPingSummary *)summary error:(NSError *)error { 130 | NSLog(@"FSENT> %@, %@", summary, error); 131 | self.failCount += 1; 132 | self.stopPingCount += 1; 133 | [self checkFinalStatusWith:pinger andSummert:summary]; 134 | } 135 | 136 | 137 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 138 | [super setSelected:selected animated:animated]; 139 | 140 | NSLog(@"Selected"); 141 | // Configure the view for the selected state 142 | } 143 | 144 | 145 | 146 | -(void)makeCheck 147 | { 148 | if (self.checked) { 149 | 150 | }else{ 151 | [self doCheck]; 152 | } 153 | } 154 | 155 | -(void)uncheck 156 | { 157 | self.checked = NO; 158 | self.checkStatusIcon.image = nil; 159 | } 160 | 161 | -(void)doCheck{ 162 | 163 | self.checked = YES; 164 | self.checkStatusIcon.image = [UIImage imageNamed:@"Check"]; 165 | [[NSNotificationCenter defaultCenter] postNotificationName:CCNFilterStationStatus object:self.stationDic]; 166 | } 167 | 168 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 169 | { 170 | 171 | POPSpringAnimation *anim = 172 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 173 | anim.springBounciness = 10; 174 | anim.springSpeed = 10; 175 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 176 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)]; 177 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleTo"]; 178 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 179 | if (finished) { 180 | 181 | } 182 | }; 183 | 184 | [super touchesBegan:touches withEvent:event]; 185 | } 186 | 187 | -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 188 | 189 | POPSpringAnimation *anim = 190 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 191 | anim.springBounciness = 10; 192 | anim.springSpeed = 10; 193 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)]; 194 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 195 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 196 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 197 | if (finished) { 198 | 199 | } 200 | }; 201 | 202 | [super touchesEnded:touches withEvent:event]; 203 | } 204 | 205 | -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 206 | { 207 | POPSpringAnimation *anim = 208 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 209 | anim.springBounciness = 10; 210 | anim.springSpeed = 10; 211 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)]; 212 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 213 | [self.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 214 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 215 | if (finished) { 216 | 217 | } 218 | }; 219 | 220 | [super touchesCancelled:touches withEvent:event]; 221 | } 222 | 223 | @end 224 | -------------------------------------------------------------------------------- /Crashlytics.framework/Versions/A/Headers/Crashlytics.h: -------------------------------------------------------------------------------- 1 | // 2 | // Crashlytics.h 3 | // Crashlytics 4 | // 5 | // Copyright 2013 Crashlytics, Inc. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | * 12 | * The CLS_LOG macro provides as easy way to gather more information in your log messages that are 13 | * sent with your crash data. CLS_LOG prepends your custom log message with the function name and 14 | * line number where the macro was used. If your app was built with the DEBUG preprocessor macro 15 | * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog. 16 | * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only. 17 | * 18 | * Example output: 19 | * -[AppDelegate login:] line 134 $ login start 20 | * 21 | * If you would like to change this macro, create a new header file, unset our define and then define 22 | * your own version. Make sure this new header file is imported after the Crashlytics header file. 23 | * 24 | * #undef CLS_LOG 25 | * #define CLS_LOG(__FORMAT__, ...) CLSNSLog... 26 | * 27 | **/ 28 | #ifdef DEBUG 29 | #define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 30 | #else 31 | #define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 32 | #endif 33 | 34 | /** 35 | * 36 | * Add logging that will be sent with your crash data. This logging will not show up in the system.log 37 | * and will only be visible in your Crashlytics dashboard. 38 | * 39 | **/ 40 | OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 41 | OBJC_EXTERN void CLSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0); 42 | 43 | /** 44 | * 45 | * Add logging that will be sent with your crash data. This logging will show up in the system.log 46 | * and your Crashlytics dashboard. It is not recommended for Release builds. 47 | * 48 | **/ 49 | OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 50 | OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0); 51 | 52 | 53 | @protocol CrashlyticsDelegate; 54 | 55 | @interface Crashlytics : NSObject 56 | 57 | @property (nonatomic, readonly, copy) NSString *apiKey; 58 | @property (nonatomic, readonly, copy) NSString *version; 59 | @property (nonatomic, assign) BOOL debugMode; 60 | 61 | @property (nonatomic, assign) NSObject *delegate; 62 | 63 | /** 64 | * 65 | * The recommended way to install Crashlytics into your application is to place a call 66 | * to +startWithAPIKey: in your -application:didFinishLaunchingWithOptions: method. 67 | * 68 | * This delay defaults to 1 second in order to generally give the application time to 69 | * fully finish launching. 70 | * 71 | **/ 72 | + (Crashlytics *)startWithAPIKey:(NSString *)apiKey; 73 | + (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay; 74 | 75 | /** 76 | * 77 | * If you need the functionality provided by the CrashlyticsDelegate protocol, you can use 78 | * these convenience methods to activate the framework and set the delegate in one call. 79 | * 80 | **/ 81 | + (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject *)delegate; 82 | + (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject *)delegate afterDelay:(NSTimeInterval)delay; 83 | 84 | /** 85 | * 86 | * Access the singleton Crashlytics instance. 87 | * 88 | **/ 89 | + (Crashlytics *)sharedInstance; 90 | 91 | /** 92 | * 93 | * The easiest way to cause a crash - great for testing! 94 | * 95 | **/ 96 | - (void)crash; 97 | 98 | /** 99 | * 100 | * Many of our customers have requested the ability to tie crashes to specific end-users of their 101 | * application in order to facilitate responses to support requests or permit the ability to reach 102 | * out for more information. We allow you to specify up to three separate values for display within 103 | * the Crashlytics UI - but please be mindful of your end-user's privacy. 104 | * 105 | * We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record 106 | * in your system. This could be a database id, hash, or other value that is meaningless to a 107 | * third-party observer but can be indexed and queried by you. 108 | * 109 | * Optionally, you may also specify the end-user's name or username, as well as email address if you 110 | * do not have a system that works well with obscured identifiers. 111 | * 112 | * Pursuant to our EULA, this data is transferred securely throughout our system and we will not 113 | * disseminate end-user data unless required to by law. That said, if you choose to provide end-user 114 | * contact information, we strongly recommend that you disclose this in your application's privacy 115 | * policy. Data privacy is of our utmost concern. 116 | * 117 | **/ 118 | - (void)setUserIdentifier:(NSString *)identifier; 119 | - (void)setUserName:(NSString *)name; 120 | - (void)setUserEmail:(NSString *)email; 121 | 122 | + (void)setUserIdentifier:(NSString *)identifier; 123 | + (void)setUserName:(NSString *)name; 124 | + (void)setUserEmail:(NSString *)email; 125 | 126 | /** 127 | * 128 | * Set a value for a key to be associated with your crash data. 129 | * 130 | **/ 131 | - (void)setObjectValue:(id)value forKey:(NSString *)key; 132 | - (void)setIntValue:(int)value forKey:(NSString *)key; 133 | - (void)setBoolValue:(BOOL)value forKey:(NSString *)key; 134 | - (void)setFloatValue:(float)value forKey:(NSString *)key; 135 | 136 | + (void)setObjectValue:(id)value forKey:(NSString *)key; 137 | + (void)setIntValue:(int)value forKey:(NSString *)key; 138 | + (void)setBoolValue:(BOOL)value forKey:(NSString *)key; 139 | + (void)setFloatValue:(float)value forKey:(NSString *)key; 140 | 141 | @end 142 | 143 | /** 144 | * The CLSCrashReport protocol exposes methods that you can call on crash report objects passed 145 | * to delegate methods. If you want these values or the entire object to stay in memory retain 146 | * them or copy them. 147 | **/ 148 | @protocol CLSCrashReport 149 | @required 150 | 151 | /** 152 | * Returns the session identifier for the crash report. 153 | **/ 154 | @property (nonatomic, readonly) NSString *identifier; 155 | 156 | /** 157 | * Returns the custom key value data for the crash report. 158 | **/ 159 | @property (nonatomic, readonly) NSDictionary *customKeys; 160 | 161 | /** 162 | * Returns the CFBundleVersion of the application that crashed. 163 | **/ 164 | @property (nonatomic, readonly) NSString *bundleVersion; 165 | 166 | /** 167 | * Returns the CFBundleShortVersionString of the application that crashed. 168 | **/ 169 | @property (nonatomic, readonly) NSString *bundleShortVersionString; 170 | 171 | /** 172 | * Returns the date that the application crashed at. 173 | **/ 174 | @property (nonatomic, readonly) NSDate *crashedOnDate; 175 | 176 | /** 177 | * Returns the os version that the application crashed on. 178 | **/ 179 | @property (nonatomic, readonly) NSString *OSVersion; 180 | 181 | /** 182 | * Returns the os build version that the application crashed on. 183 | **/ 184 | @property (nonatomic, readonly) NSString *OSBuildVersion; 185 | 186 | @end 187 | 188 | /** 189 | * 190 | * The CrashlyticsDelegate protocol provides a mechanism for your application to take 191 | * action on events that occur in the Crashlytics crash reporting system. You can make 192 | * use of these calls by assigning an object to the Crashlytics' delegate property directly, 193 | * or through the convenience startWithAPIKey:delegate:... methods. 194 | * 195 | **/ 196 | @protocol CrashlyticsDelegate 197 | @optional 198 | 199 | /** 200 | * 201 | * Called once a Crashlytics instance has determined that the last execution of the 202 | * application ended in a crash. This is called some time after the crash reporting 203 | * process has begun. If you have specified a delay in one of the 204 | * startWithAPIKey:... calls, this will take at least that long to be invoked. 205 | * 206 | **/ 207 | - (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics; 208 | 209 | /** 210 | * 211 | * Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is 212 | * called once a Crashlytics instance has determined that the last execution of the 213 | * application ended in a crash. A CLSCrashReport is passed back that contains data about 214 | * the last crash report that was generated. See the CLSCrashReport protocol for method details. 215 | * This method is called after crashlyticsDidDetectCrashDuringPreviousExecution. 216 | * 217 | **/ 218 | - (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id )crash; 219 | 220 | @end 221 | 222 | /** 223 | * `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()` 224 | */ 225 | #define CrashlyticsKit [Crashlytics sharedInstance] 226 | -------------------------------------------------------------------------------- /Today/TodayViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TodayViewController.m 3 | // Today 4 | // 5 | // Created by NIX on 14/12/26. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "TodayViewController.h" 10 | #import 11 | #import "VPNStations.h" 12 | #import "VCIPsecVPNManager.h" 13 | #import "VPNButton.h" 14 | #import "HydroHelper.h" 15 | #import 16 | 17 | @interface TodayViewController () 18 | 19 | @property (strong, nonatomic) IBOutletCollection(VPNButton) NSArray *vpnButtons; 20 | 21 | @property (nonatomic, strong) VPNButton *currentButton; 22 | 23 | @property (nonatomic, strong) NSArray *vpnStations; 24 | 25 | @property (nonatomic, strong) VCIPsecVPNManager *vpnManager; 26 | 27 | @property (nonatomic) BOOL isPrepareProfile; 28 | 29 | @end 30 | 31 | @implementation TodayViewController 32 | 33 | - (void)awakeFromNib { 34 | [super awakeFromNib]; 35 | 36 | self.preferredContentSize = CGSizeMake(0, 80); 37 | 38 | } 39 | 40 | - (id)initWithCoder:(NSCoder *)aDecoder { 41 | if (self = [super initWithCoder:aDecoder]) { 42 | [[NSNotificationCenter defaultCenter] addObserver:self 43 | selector:@selector(userDefaultsDidChange:) 44 | name:NSUserDefaultsDidChangeNotification 45 | object:nil]; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)userDefaultsDidChange:(NSNotification *)notification { 51 | 52 | #ifdef DEBUG 53 | NSLog(@"Active Today"); 54 | #endif 55 | [self checkStatus]; 56 | } 57 | 58 | -(void)checkStatus 59 | { 60 | NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 61 | BOOL active = [defaults boolForKey:@"ActiveToday"]; 62 | 63 | if (active) { 64 | self.country1.hidden = NO; 65 | self.country2.hidden = NO; 66 | self.country3.hidden = NO; 67 | self.country4.hidden = NO; 68 | self.signInLabel.alpha = 0; 69 | }else{ 70 | self.country1.hidden = YES; 71 | self.country2.hidden = YES; 72 | self.country3.hidden = YES; 73 | self.country4.hidden = YES; 74 | self.signInLabel.alpha = 1; 75 | } 76 | } 77 | 78 | - (void)viewDidLoad { 79 | [super viewDidLoad]; 80 | // Do any additional setup after loading the view from its nib. 81 | [Crashlytics startWithAPIKey:@"de004490005a062fa95a4d5676a7edbfbe42c582"]; 82 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshUI) name:NEVPNStatusDidChangeNotification object:nil]; 83 | [self checkStatus]; 84 | 85 | if (SCREEN_WIDTH <= 320) { 86 | 87 | #ifdef DEBUG 88 | NSLog(@"Relayout"); 89 | #endif 90 | self.country2SpaceConstraint.constant = 7.0; 91 | self.country3SpaceConstraint.constant = 7.0; 92 | self.countrySpaceConstraint.constant = 7.0; 93 | 94 | [self.view layoutIfNeeded]; 95 | 96 | } 97 | } 98 | 99 | - (void)dealloc 100 | { 101 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 102 | } 103 | 104 | - (NSArray *)vpnStations 105 | { 106 | if (!_vpnStations) { 107 | _vpnStations = [VPNStations sharedInstance].stations; 108 | } 109 | return _vpnStations; 110 | } 111 | 112 | - (VCIPsecVPNManager *)vpnManager 113 | { 114 | if (!_vpnManager) { 115 | _vpnManager = [[VCIPsecVPNManager alloc] init]; 116 | } 117 | return _vpnManager; 118 | } 119 | 120 | - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { 121 | // Perform any setup necessary in order to update the view. 122 | 123 | // If an error is encountered, use NCUpdateResultFailed 124 | // If there's no update required, use NCUpdateResultNoData 125 | // If there's an update, use NCUpdateResultNewData 126 | 127 | completionHandler(NCUpdateResultNoData); 128 | } 129 | 130 | - (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets 131 | { 132 | return UIEdgeInsetsMake(0, defaultMarginInsets.left, 0, 0); 133 | } 134 | 135 | - (void)viewDidLayoutSubviews 136 | { 137 | #ifdef DEBUG 138 | NSLog(@"bounds %@", NSStringFromCGRect(self.view.bounds)); 139 | #endif 140 | 141 | [self.vpnButtons enumerateObjectsUsingBlock:^(VPNButton *button, NSUInteger idx, BOOL *stop) { 142 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectInset(button.bounds, 13, 13)]; 143 | imageView.contentMode = UIViewContentModeScaleAspectFit; 144 | imageView.image = [UIImage imageNamed:[self.vpnStations[idx] valueForKey:@"short_name"]]; 145 | [button addSubview:imageView]; 146 | }]; 147 | 148 | [self.vpnManager prepareWithCompletion:^(NSError *error) { 149 | if (self.vpnManager.vpnManager.connection.status == NEVPNStatusConnected) { 150 | 151 | NSString *host= self.vpnManager.vpnManager.protocol.serverAddress; 152 | 153 | for (NSInteger i = 0; i < self.vpnStations.count; i++) { 154 | NSDictionary *station = self.vpnStations[i]; 155 | if ([station[@"host"] isEqualToString:host]) { 156 | self.currentButton = self.vpnButtons[i]; 157 | 158 | self.currentButton.buttonState = VPNButtonStateConnected; 159 | 160 | break; 161 | } 162 | } 163 | } 164 | }]; 165 | } 166 | 167 | - (void)refreshUI 168 | { 169 | 170 | #ifdef DEBUG 171 | NSLog(@"refreshUI"); 172 | #endif 173 | 174 | [self.vpnManager prepareWithCompletion:^(NSError *error) { 175 | 176 | switch (self.vpnManager.vpnManager.connection.status) { 177 | case NEVPNStatusConnecting: 178 | NSLog(@"NEVPNStatusConnecting"); 179 | self.currentButton.buttonState = VPNButtonStateConnecting; 180 | break; 181 | 182 | case NEVPNStatusConnected: 183 | NSLog(@"NEVPNStatusConnected"); 184 | self.currentButton.buttonState = VPNButtonStateConnected; 185 | break; 186 | 187 | case NEVPNStatusDisconnecting: 188 | NSLog(@"NEVPNStatusDisconnecting"); 189 | self.currentButton.buttonState = VPNButtonStateConnecting; 190 | break; 191 | 192 | case NEVPNStatusDisconnected: 193 | NSLog(@"NEVPNStatusDisconnected"); 194 | self.currentButton.buttonState = VPNButtonStateNormal; 195 | break; 196 | 197 | case NEVPNStatusReasserting: 198 | NSLog(@"NEVPNStatusReasserting"); 199 | self.currentButton.buttonState = VPNButtonStateConnecting; 200 | break; 201 | 202 | case NEVPNStatusInvalid: 203 | NSLog(@"NEVPNStatusReasserting"); 204 | self.currentButton.buttonState = VPNButtonStateConnectFailed; 205 | 206 | default: 207 | self.currentButton.buttonState = VPNButtonStateNormal; 208 | break; 209 | } 210 | 211 | }]; 212 | } 213 | 214 | - (IBAction)pressedVPNButton:(VPNButton *)sender { 215 | 216 | #ifdef DEBUG 217 | NSLog(@"pressedVPNButton"); 218 | #endif 219 | 220 | BOOL isNewButton = NO; 221 | 222 | if (sender != self.currentButton) { 223 | isNewButton = YES; 224 | 225 | [self.vpnButtons enumerateObjectsUsingBlock:^(VPNButton *button, NSUInteger idx, BOOL *stop) { 226 | if (button != sender) { 227 | button.buttonState = VPNButtonStateNormal; 228 | } 229 | }]; 230 | } 231 | 232 | self.currentButton = sender; 233 | 234 | NSInteger indexOfButton = [self.vpnButtons indexOfObject:self.currentButton]; 235 | 236 | NSDictionary *station = self.vpnStations[indexOfButton % self.vpnStations.count]; 237 | 238 | if (self.isPrepareProfile) { 239 | return; 240 | } 241 | self.isPrepareProfile = YES; 242 | 243 | [self.vpnManager prepareWithCompletion:^(NSError *error) { 244 | self.isPrepareProfile = NO; 245 | 246 | if (error) { 247 | self.currentButton.buttonState = VPNButtonStateConnectFailed; 248 | }else { 249 | 250 | if (self.vpnManager.vpnManager.connection.status == NEVPNStatusConnected || self.vpnManager.vpnManager.connection.status == NEVPNStatusConnecting || self.vpnManager.vpnManager.connection.status == NEVPNStatusReasserting || self.vpnManager.vpnManager.connection.status == NEVPNStatusDisconnecting) { 251 | 252 | #ifdef DEBUG 253 | NSLog(@"Dicsonnect VPN"); 254 | #endif 255 | 256 | [self.vpnManager.vpnManager.connection stopVPNTunnel]; 257 | 258 | } else { 259 | 260 | #ifdef DEBUG 261 | NSLog(@"Connect VPN"); 262 | #endif 263 | 264 | [self connectVPNWithStation:station]; 265 | } 266 | } 267 | 268 | }]; 269 | } 270 | 271 | - (void)connectVPNWithStation:(NSDictionary *)station { 272 | 273 | NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 274 | BOOL active = [defaults boolForKey:@"ikev2"]; 275 | 276 | if (active) { 277 | [self.vpnManager connectIPSecIKEv2WithHost:[station valueForKey:@"host"] andUsername:[[VPNStations sharedInstance].config valueForKey:@"username"] andPassword:[[VPNStations sharedInstance].config valueForKey:@"password"] andPSK:[[VPNStations sharedInstance].config valueForKey:@"psk"] andGroupName:[[VPNStations sharedInstance].config valueForKey:@"groupname"]]; 278 | } else { 279 | [self.vpnManager connectIPSecWithHost:[station valueForKey:@"host"] andUsername:[[VPNStations sharedInstance].config valueForKey:@"username"] andPassword:[[VPNStations sharedInstance].config valueForKey:@"password"] andPSK:[[VPNStations sharedInstance].config valueForKey:@"psk"] andGroupName:[[VPNStations sharedInstance].config valueForKey:@"groupname"]]; 280 | } 281 | 282 | } 283 | 284 | 285 | @end 286 | -------------------------------------------------------------------------------- /Hydro/VCIPsecVPNManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCIPsecVPNManager.m 3 | // VPNCloud 4 | // 5 | // Created by kevinzhow on 14/11/12. 6 | // Copyright (c) 2014年 Kingaxis Inc. All rights reserved. 7 | // 8 | 9 | #import "VCIPsecVPNManager.h" 10 | #import "VCKetChain.h" 11 | 12 | static NSString * const IKEv1ServiceName = @"Hydro VPN"; 13 | static NSString * const IKEv2ServiceName = @"Hydro IKEv2 VPN"; 14 | 15 | @implementation VCIPsecVPNManager 16 | 17 | -(id)init 18 | { 19 | self = [super init]; 20 | if (self) { 21 | self.vpnManager = [NEVPNManager sharedManager]; 22 | 23 | } 24 | return self; 25 | 26 | } 27 | 28 | 29 | 30 | -(void)prepareWithCompletion:(void (^)(NSError *))done 31 | { 32 | // init VPN manager 33 | 34 | 35 | [_vpnManager loadFromPreferencesWithCompletionHandler:^(NSError *error) { 36 | 37 | [self updateServiceStatus]; 38 | 39 | done(error); 40 | 41 | }]; 42 | 43 | } 44 | 45 | -(void)updateServiceStatus 46 | { 47 | // NSLog(@"VPN is %@ %@",_vpnManager.protocol, [_vpnManager.protocol description]); 48 | if ([[[self.vpnManager.protocol valueForKey:@"type"] description] isEqualToString:@"5"]) { 49 | self.IKEv2Enabled = YES; 50 | self.IKEv1Enabled = NO; 51 | }else if ([[[self.vpnManager.protocol valueForKey:@"type"] description] isEqualToString:@"1"]){ 52 | self.IKEv2Enabled = NO; 53 | self.IKEv1Enabled = YES; 54 | } 55 | } 56 | 57 | -(void)connectIPSecIKEv2WithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andPSK:(NSString *)psk andGroupName:(NSString *)groupName 58 | 59 | { 60 | VCKetChain * keychain =[[VCKetChain alloc] initWithService:@"VPNIKEv2PSK" withGroup:nil]; 61 | 62 | NSString *key =@"password"; 63 | NSData * value = [password dataUsingEncoding:NSUTF8StringEncoding]; 64 | 65 | NSString *key2 =@"psk"; 66 | NSData * value2 = [psk dataUsingEncoding:NSUTF8StringEncoding]; 67 | 68 | if(![keychain find:@"password"] || ![keychain find:@"psk"]) 69 | { 70 | if ([keychain insert:key :value]) { 71 | NSLog(@"Successfully added data"); 72 | }else{ 73 | NSLog(@"Failed to add data"); 74 | } 75 | 76 | if ([keychain insert:key2 :value2]) { 77 | NSLog(@"Successfully added data shared key"); 78 | }else{ 79 | NSLog(@"Failed to add data shared key"); 80 | } 81 | 82 | } 83 | else 84 | { 85 | NSLog(@"No need to add data"); 86 | } 87 | 88 | 89 | NEVPNProtocolIKEv2 *p = [[NEVPNProtocolIKEv2 alloc] init]; 90 | p.username = username; 91 | p.serverAddress = host; 92 | p.passwordReference = [keychain find:@"password"]; 93 | 94 | 95 | p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret; 96 | p.sharedSecretReference = [keychain find:@"psk"]; 97 | 98 | p.localIdentifier = groupName; 99 | p.remoteIdentifier = host; 100 | 101 | p.useExtendedAuthentication = YES; 102 | p.disconnectOnSleep = NO; 103 | 104 | _vpnManager.protocol = p; 105 | _vpnManager.localizedDescription = IKEv2ServiceName; 106 | _vpnManager.enabled = YES; 107 | 108 | 109 | // NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new]; 110 | // connectRule.interfaceTypeMatch = NEOnDemandRuleInterfaceTypeCellular | NEOnDemandRuleInterfaceTypeWiFi; 111 | // 112 | // 113 | // NEOnDemandRuleEvaluateConnection * domainRule = [NEOnDemandRuleEvaluateConnection new]; 114 | // 115 | // NEEvaluateConnectionRule * domainMatch = [[NEEvaluateConnectionRule alloc] 116 | // initWithMatchDomains: 117 | // @[@"*.twitter.com", @"www.twitter.com", @"*.google.com", @"*.google.com.hk", @"*.youtube.com", 118 | // @"*.googleusercontent.com",@"*.gstatic.com", @"*.ggpht.com",@"*.appspot.com", @"*.googleapis.com", @"*.google.cn", 119 | // @"*.fbcdn.net", @"*.staticflickr.com", @"*.twimg.com", @"*.ytimg.com", @"*.feedly.com",@"*.tinypic.com", @"*.instagram.com"] andAction:NEEvaluateConnectionRuleActionConnectIfNeeded]; 120 | // domainRule.connectionRules = @[domainMatch]; 121 | // 122 | // 123 | // [_vpnManager setOnDemandRules:@[domainRule]]; 124 | 125 | // _vpnManager.onDemandEnabled = YES; 126 | 127 | [_vpnManager saveToPreferencesWithCompletionHandler:^(NSError *error) { 128 | 129 | #ifdef DEBUG 130 | NSLog(@"Save config failed [%@]", error.localizedDescription); 131 | #endif 132 | if (!error) { 133 | #ifdef DEBUG 134 | NSLog(@"username: %@", [_vpnManager protocol].username); 135 | NSLog(@"password: %@ %@", [_vpnManager protocol].passwordReference, [keychain find:@"password"]); 136 | #endif 137 | } 138 | 139 | NSError *startError; 140 | [_vpnManager.connection startVPNTunnelAndReturnError:&startError]; 141 | if (startError) { 142 | 143 | #ifdef DEBUG 144 | NSLog(@"Start VPN failed: [%@]", startError.localizedDescription); 145 | #endif 146 | } 147 | }]; 148 | } 149 | 150 | 151 | -(void)connectIPSecIKEv2WithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andP12Name:(NSString *)p12Name andidentityDataPassword:(NSString *)identityDataPassword andGroupName:(NSString *)groupName 152 | { 153 | 154 | VCKetChain * keychain =[[VCKetChain alloc] initWithService:@"VPNIKEv2" withGroup:nil]; 155 | 156 | NSString *key =@"password"; 157 | NSData * value = [password dataUsingEncoding:NSUTF8StringEncoding]; 158 | 159 | if(![keychain find:@"password"]) 160 | { 161 | if ([keychain insert:key :value]) { 162 | NSLog(@"Successfully added data"); 163 | }else{ 164 | NSLog(@"Failed to add data"); 165 | } 166 | 167 | } 168 | else 169 | { 170 | NSLog(@"No need to add data"); 171 | } 172 | 173 | 174 | NEVPNProtocolIKEv2 *p = [[NEVPNProtocolIKEv2 alloc] init]; 175 | p.username = username; 176 | p.serverAddress = host; 177 | p.serverCertificateIssuerCommonName = @"COMODO RSA Domain Validation Secure Server CA"; 178 | p.serverCertificateCommonName = @"*.piner.me"; 179 | p.passwordReference = [keychain find:@"password"]; 180 | 181 | 182 | p.identityData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@",p12Name] ofType:@"p12"]]; 183 | p.identityDataPassword = identityDataPassword; 184 | p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate; 185 | 186 | p.localIdentifier = groupName; 187 | p.remoteIdentifier = host; 188 | 189 | p.useExtendedAuthentication = YES; 190 | p.disconnectOnSleep = NO; 191 | 192 | _vpnManager.protocol = p; 193 | _vpnManager.localizedDescription = IKEv2ServiceName; 194 | _vpnManager.enabled = YES; 195 | 196 | 197 | // NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new]; 198 | // connectRule.interfaceTypeMatch = NEOnDemandRuleInterfaceTypeCellular | NEOnDemandRuleInterfaceTypeWiFi; 199 | // 200 | // 201 | NEOnDemandRuleEvaluateConnection * domainRule = [NEOnDemandRuleEvaluateConnection new]; 202 | 203 | NEEvaluateConnectionRule * domainMatch = [[NEEvaluateConnectionRule alloc] 204 | initWithMatchDomains: 205 | @[@"*.twitter.com", @"www.twitter.com", @"*.google.com", @"*.google.com.hk", @"*.youtube.com", 206 | @"*.googleusercontent.com",@"*.gstatic.com", @"*.ggpht.com",@"*.appspot.com", @"*.googleapis.com", @"*.google.cn", 207 | @"*.fbcdn.net", @"*.staticflickr.com", @"*.twimg.com", @"*.ytimg.com", @"*.feedly.com",@"*.tinypic.com", @"*.instagram.com"] andAction:NEEvaluateConnectionRuleActionConnectIfNeeded]; 208 | domainRule.connectionRules = @[domainMatch]; 209 | 210 | 211 | [_vpnManager setOnDemandRules:@[domainRule]]; 212 | 213 | // _vpnManager.onDemandEnabled = YES; 214 | 215 | [_vpnManager saveToPreferencesWithCompletionHandler:^(NSError *error) { 216 | 217 | #ifdef DEBUG 218 | NSLog(@"Save config failed [%@]", error.localizedDescription); 219 | #endif 220 | if (!error) { 221 | #ifdef DEBUG 222 | NSLog(@"username: %@", [_vpnManager protocol].username); 223 | NSLog(@"password: %@ %@", [_vpnManager protocol].passwordReference, [keychain find:@"password"]); 224 | #endif 225 | 226 | } 227 | 228 | NSError *startError; 229 | [_vpnManager.connection startVPNTunnelAndReturnError:&startError]; 230 | if (startError) { 231 | 232 | #ifdef DEBUG 233 | NSLog(@"Start VPN failed: [%@]", startError.localizedDescription); 234 | #endif 235 | } 236 | 237 | }]; 238 | 239 | } 240 | 241 | 242 | -(void)connectIPSecWithHost:(NSString *)host andUsername:(NSString *)username andPassword:(NSString *)password andPSK:(NSString *)psk andGroupName:(NSString *)groupName 243 | { 244 | 245 | 246 | VCKetChain * keychain =[[VCKetChain alloc] initWithService:@"VPN" withGroup:nil]; 247 | 248 | NSString *key =@"password"; 249 | NSData * value = [password dataUsingEncoding:NSUTF8StringEncoding]; 250 | 251 | NSString *key2 =@"psk"; 252 | NSData * value2 = [psk dataUsingEncoding:NSUTF8StringEncoding]; 253 | 254 | if(![keychain find:@"password"] || ![keychain find:@"psk"]) 255 | { 256 | if ([keychain insert:key :value]) { 257 | NSLog(@"Successfully added data"); 258 | }else{ 259 | NSLog(@"Failed to add data"); 260 | } 261 | 262 | if ([keychain insert:key2 :value2]) { 263 | NSLog(@"Successfully added data shared key"); 264 | }else{ 265 | NSLog(@"Failed to add data shared key"); 266 | } 267 | 268 | } 269 | else 270 | { 271 | NSLog(@"No need to add data"); 272 | } 273 | 274 | 275 | NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init]; 276 | p.username = username; 277 | p.serverAddress = host; 278 | p.passwordReference = [keychain find:@"password"]; 279 | 280 | // PSK 281 | p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret; 282 | p.sharedSecretReference = [keychain find:@"psk"]; 283 | 284 | 285 | /* 286 | // certificate 287 | p.identityData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]]; 288 | p.identityDataPassword = @"[Your certificate import password]"; 289 | */ 290 | 291 | p.remoteIdentifier = host; 292 | p.localIdentifier = groupName; 293 | 294 | p.useExtendedAuthentication = YES; 295 | p.disconnectOnSleep = NO; 296 | 297 | _vpnManager.protocol = p; 298 | _vpnManager.localizedDescription = IKEv1ServiceName; 299 | _vpnManager.enabled = YES; 300 | _vpnManager.onDemandEnabled = NO; 301 | 302 | 303 | NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new]; 304 | connectRule.interfaceTypeMatch = NEOnDemandRuleInterfaceTypeCellular | NEOnDemandRuleInterfaceTypeWiFi; 305 | 306 | 307 | 308 | [_vpnManager setOnDemandRules:@[connectRule]]; 309 | 310 | 311 | [_vpnManager saveToPreferencesWithCompletionHandler:^(NSError *error) { 312 | 313 | #ifdef DEBUG 314 | NSLog(@"Save config failed [%@]", error.localizedDescription); 315 | #endif 316 | if (!error) { 317 | #ifdef DEBUG 318 | NSLog(@"username: %@", [_vpnManager protocol].username); 319 | NSLog(@"password: %@ %@", [_vpnManager protocol].passwordReference, [keychain find:@"password"]); 320 | NSLog(@"sharedSecretReference: %@ %@", p.sharedSecretReference, [keychain find:@"psk"]); 321 | #endif 322 | 323 | NSError *startError; 324 | [_vpnManager.connection startVPNTunnelAndReturnError:&startError]; 325 | if (startError) { 326 | NSLog(@"Start VPN failed: [%@]", startError.localizedDescription); 327 | } 328 | } 329 | }]; 330 | 331 | 332 | } 333 | 334 | @end 335 | -------------------------------------------------------------------------------- /Today/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 35 | 48 | 61 | 71 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Hydro/Views/InviteView.m: -------------------------------------------------------------------------------- 1 | // 2 | // InviteView.m 3 | // Hydro 4 | // 5 | // Created by NIX on 14/12/27. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "InviteView.h" 10 | 11 | @interface InviteView() 12 | 13 | @end 14 | 15 | 16 | @implementation InviteView 17 | 18 | - (instancetype)initWithFrame:(CGRect)frame 19 | { 20 | if (self = [super initWithFrame:frame]) { 21 | [self initUI]; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)initUI 27 | { 28 | CGFloat logoSize = 36.0; 29 | 30 | //NSNumber *leftInset = @20; 31 | //NSNumber *rightInset = @30; 32 | 33 | CGFloat fontSize = 18.0; 34 | 35 | #pragma mark - Invite Text Filed 36 | 37 | UILabel *inviteTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 38 | inviteTextLabel.font = [UIFont fontWithName:@"Avenir-Light" size:fontSize]; 39 | inviteTextLabel.textColor = [UIColor colorWithWhite:1.0 alpha:0.9]; 40 | inviteTextLabel.text = NSLocalizedString(@"Invite Friend to", nil); 41 | [self addSubview:inviteTextLabel]; 42 | 43 | UIImageView *logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, logoSize, logoSize)]; 44 | logoImageView.contentMode = UIViewContentModeScaleAspectFit; 45 | logoImageView.image = [UIImage imageNamed:@"logo"]; 46 | [self addSubview:logoImageView]; 47 | 48 | UILabel *hydroLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 49 | hydroLabel.font = [UIFont fontWithName:@"Avenir-Medium" size:fontSize]; 50 | hydroLabel.textColor = [UIColor whiteColor]; 51 | hydroLabel.text = NSLocalizedString(@"Hydro", nil); 52 | [self addSubview:hydroLabel]; 53 | 54 | UIView *helperView = [[UIView alloc] init]; 55 | [self addSubview:helperView]; 56 | 57 | { 58 | inviteTextLabel.translatesAutoresizingMaskIntoConstraints = NO; 59 | logoImageView.translatesAutoresizingMaskIntoConstraints = NO; 60 | hydroLabel.translatesAutoresizingMaskIntoConstraints = NO; 61 | helperView.translatesAutoresizingMaskIntoConstraints = NO; 62 | 63 | NSDictionary *viewsDictionary = @{ 64 | @"inviteTextLabel":inviteTextLabel, 65 | @"logoImageView":logoImageView, 66 | @"hydroLabel":hydroLabel, 67 | }; 68 | 69 | NSNumber *topOffset = @40; 70 | NSNumber *logoWidth = @(logoSize); 71 | NSNumber *logoHeight = @(logoSize); 72 | NSNumber *labelHeight = @(logoSize); 73 | 74 | 75 | NSArray *constraintH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[inviteTextLabel]-[logoImageView(logoWidth)]-[hydroLabel]-(>=0)-|" 76 | options:0 77 | metrics:@{@"logoWidth": logoWidth, 78 | } 79 | views:viewsDictionary]; 80 | 81 | 82 | NSArray *constraintV1 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(topOffset)-[inviteTextLabel(labelHeight)]" 83 | options:0 84 | metrics:@{@"topOffset": topOffset, 85 | @"labelHeight": labelHeight, 86 | } 87 | views:viewsDictionary]; 88 | 89 | NSArray *constraintV2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(topOffset)-[logoImageView(logoHeight)]" 90 | options:0 91 | metrics:@{@"topOffset": topOffset, 92 | @"logoHeight": logoHeight, 93 | } 94 | views:viewsDictionary]; 95 | 96 | NSArray *constraintV3 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(topOffset)-[hydroLabel(labelHeight)]" 97 | options:0 98 | metrics:@{@"topOffset": topOffset, 99 | @"labelHeight": labelHeight, 100 | } 101 | views:viewsDictionary]; 102 | 103 | 104 | 105 | 106 | NSLayoutConstraint *helpConstraint1 = [NSLayoutConstraint constraintWithItem:helperView 107 | attribute:NSLayoutAttributeLeading 108 | relatedBy:NSLayoutRelationEqual 109 | toItem:inviteTextLabel 110 | attribute:NSLayoutAttributeLeading 111 | multiplier:1 112 | constant:0]; 113 | 114 | NSLayoutConstraint *helpConstraint2 = [NSLayoutConstraint constraintWithItem:helperView 115 | attribute:NSLayoutAttributeTrailing 116 | relatedBy:NSLayoutRelationEqual 117 | toItem:hydroLabel 118 | attribute:NSLayoutAttributeTrailing 119 | multiplier:1 120 | constant:0]; 121 | 122 | NSLayoutConstraint *helpConstraint3 = [NSLayoutConstraint constraintWithItem:helperView 123 | attribute:NSLayoutAttributeCenterX 124 | relatedBy:NSLayoutRelationEqual 125 | toItem:self 126 | attribute:NSLayoutAttributeCenterX 127 | multiplier:1 128 | constant:0]; 129 | 130 | [self addConstraints:constraintH]; 131 | [self addConstraints:constraintV1]; 132 | [self addConstraints:constraintV2]; 133 | [self addConstraints:constraintV3]; 134 | [self addConstraint:helpConstraint1]; 135 | [self addConstraint:helpConstraint2]; 136 | [self addConstraint:helpConstraint3]; 137 | } 138 | 139 | #pragma mark - Email Text Filed 140 | 141 | _emailTextField = [[SAMTextField alloc] initWithFrame:CGRectZero]; 142 | [_emailTextField setBackground:[UIImage imageNamed:@"EM"]]; 143 | _emailTextField.textColor = [UIColor whiteColor]; 144 | _emailTextField.font = [UIFont fontWithName:@"Avenir-Medium" size:20.0]; 145 | _emailTextField.textAlignment = NSTextAlignmentCenter; 146 | _emailTextField.contentMode = UIViewContentModeScaleAspectFit; 147 | _emailTextField.adjustsFontSizeToFitWidth = YES; 148 | _emailTextField.minimumFontSize = 12; 149 | _emailTextField.tintColor = [UIColor whiteColor]; 150 | _emailTextField.returnKeyType = UIReturnKeyDone; 151 | 152 | NSDictionary *attrsDictionary = @{ 153 | NSFontAttributeName: [UIFont fontWithName:@"Avenir-Medium" size:22.0], 154 | NSForegroundColorAttributeName: [UIColor colorWithWhite:1.0 alpha:0.8] 155 | }; 156 | 157 | NSAttributedString *attrEmailString = [[NSAttributedString alloc] initWithString:@"Email" 158 | attributes:attrsDictionary]; 159 | 160 | _emailTextField.attributedPlaceholder = attrEmailString; 161 | 162 | _emailTextField.textEdgeInsets = UIEdgeInsetsMake(0.0f, 40.0f, 5.0f, 10.0f); 163 | 164 | [self addSubview:_emailTextField]; 165 | 166 | { 167 | _emailTextField.translatesAutoresizingMaskIntoConstraints = NO; 168 | 169 | NSDictionary *viewsDictionary = @{ 170 | @"inviteTextLabel":inviteTextLabel, 171 | @"emailTextField":_emailTextField, 172 | }; 173 | 174 | NSArray *constraintH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[emailTextField(240)]-(>=0)-|" 175 | options:0 176 | metrics:nil 177 | views:viewsDictionary]; 178 | 179 | NSArray *constraintV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[inviteTextLabel]-(60)-[emailTextField(30)]" 180 | options:0 181 | metrics:nil 182 | views:viewsDictionary]; 183 | 184 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_emailTextField 185 | attribute:NSLayoutAttributeCenterX 186 | relatedBy:NSLayoutRelationEqual 187 | toItem:self 188 | attribute:NSLayoutAttributeCenterX 189 | multiplier:1 190 | constant:0]; 191 | 192 | [self addConstraints:constraintH]; 193 | [self addConstraints:constraintV]; 194 | [self addConstraint:constraint]; 195 | } 196 | 197 | #pragma mark - Invite Button 198 | 199 | _inviteButton = [UIButton buttonWithType:UIButtonTypeCustom]; 200 | [_inviteButton setBackgroundImage:[UIImage imageNamed:@"Button"] forState:UIControlStateNormal]; 201 | [_inviteButton setTitle:NSLocalizedString(@"Send invitation", nil) forState:UIControlStateNormal]; 202 | _inviteButton.titleLabel.font = [UIFont fontWithName:@"Avenir-Medium" size:fontSize]; 203 | [self addSubview:_inviteButton]; 204 | 205 | { 206 | _inviteButton.translatesAutoresizingMaskIntoConstraints = NO; 207 | 208 | NSDictionary *viewsDictionary = @{ 209 | @"emailTextField":_emailTextField, 210 | @"inviteButton":_inviteButton, 211 | }; 212 | 213 | NSArray *constraintH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=0)-[inviteButton(240)]-(>=0)-|" 214 | options:0 215 | metrics:nil 216 | views:viewsDictionary]; 217 | 218 | NSArray *constraintV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[emailTextField]-(60)-[inviteButton(60)]-(>=0)-|" 219 | options:0 220 | metrics:nil 221 | views:viewsDictionary]; 222 | 223 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_inviteButton 224 | attribute:NSLayoutAttributeCenterX 225 | relatedBy:NSLayoutRelationEqual 226 | toItem:self 227 | attribute:NSLayoutAttributeCenterX 228 | multiplier:1 229 | constant:0]; 230 | 231 | [self addConstraints:constraintH]; 232 | [self addConstraints:constraintV]; 233 | [self addConstraint:constraint]; 234 | } 235 | 236 | } 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /Hydro/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | -------------------------------------------------------------------------------- /Hydro/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Hydro 4 | // 5 | // Created by kevinzhow on 14/12/25. 6 | // Copyright (c) 2014年 Catch Inc. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LoginViewController.h" 11 | #import 12 | #import "StationTableViewCell.h" 13 | #import "GVUserDefaults+Hydro.h" 14 | #import "VPNStations.h" 15 | #import "InviteView.h" 16 | #import 17 | #import 18 | 19 | #define TableViewTopConstraintInitialConstant (-400.0) 20 | 21 | @interface ViewController () 22 | 23 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewTopConstraint; 24 | 25 | @property (nonatomic) NSArray *vpnStations; 26 | @property (nonatomic) NSDictionary *stationDic; 27 | 28 | @property (nonatomic, weak) InviteView *inviteView; 29 | 30 | @property (nonatomic) HydroButton *connectButton; 31 | @property (nonatomic) UIImageView *mapImageVIew; 32 | 33 | @property (nonatomic) UILabel *donaterLabel; 34 | 35 | @property (nonatomic) VCIPsecVPNManager * vpnmanager; 36 | 37 | @property (nonatomic) NSString * domain; 38 | 39 | @property (nonatomic) NSDictionary * currentStationDict; 40 | 41 | @property (nonatomic) stationView * stationViewOne; 42 | 43 | @property (nonatomic) locationView * stationLocationView; 44 | 45 | @property (nonatomic) BOOL userHasLogined; 46 | 47 | @property (nonatomic) BOOL userCanInvite; 48 | 49 | @property (nonatomic) BOOL stationShowed; 50 | 51 | @property (nonatomic) BOOL isPrepareProfile; 52 | 53 | 54 | - (IBAction)doConnect:(id)sender; 55 | 56 | @end 57 | 58 | @implementation ViewController 59 | 60 | - (NSArray *)vpnStations 61 | { 62 | if (!_vpnStations) { 63 | _vpnStations = [VPNStations sharedInstance].stations; 64 | } 65 | return _vpnStations; 66 | } 67 | 68 | - (void)viewDidLoad { 69 | [super viewDidLoad]; 70 | 71 | self.mapImageVIew = [[UIImageView alloc] initWithFrame:CGRectMake(0, -100, SCREEN_WIDTH, 267)]; 72 | self.mapImageVIew.image = [UIImage imageNamed:@"World Map"]; 73 | self.mapImageVIew.contentMode = UIViewContentModeScaleAspectFit; 74 | [self.view addSubview:self.mapImageVIew]; 75 | 76 | 77 | UIView * headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 10)]; 78 | headerView.backgroundColor = [UIColor clearColor]; 79 | self.stationLocationView = [[locationView alloc] initWithFrame:CGRectMake(self.mapImageVIew.frame.size.width /2.0 - 15.0, self.mapImageVIew.frame.size.height /2.0 - 15.0, 30.0, 30.0)]; 80 | self.stationLocationView.alpha = 0.0; 81 | [self.mapImageVIew addSubview:self.stationLocationView]; 82 | 83 | 84 | self.stationViewOne = [[stationView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 210.0)]; 85 | [self.view addSubview:self.stationViewOne]; 86 | self.stationViewOne.alpha = 0; 87 | 88 | //self.stationDelegate = [[stationsDelegate alloc] init]; 89 | self.connectButton = [HydroButton buttonWithType:UIButtonTypeCustom]; 90 | [self.connectButton.titleLabel setFont:[UIFont fontWithName:@"Avenir-Medium" size:17.0]]; 91 | 92 | 93 | 94 | self.connectButton.frame = CGRectMake(SCREEN_WIDTH/2.0 - 120 , SCREEN_HEIGHT + 140, 240, 60); 95 | 96 | self.donaterLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, (SCREEN_HEIGHT - 160.0) , SCREEN_WIDTH, 30)]; 97 | self.donaterLabel.alpha = 0; 98 | self.donaterLabel.textColor = [UIColor colorWithWhite:1.0 alpha:0.6]; 99 | self.donaterLabel.textAlignment = NSTextAlignmentCenter; 100 | [self.donaterLabel setFont:[UIFont fontWithName:@"Avenir-Medium" size:14.0]]; 101 | 102 | [self.view addSubview:self.donaterLabel]; 103 | 104 | [self.connectButton setBackgroundImage:[UIImage imageNamed:@"Button + Connect"] forState:UIControlStateNormal]; 105 | [self.view addSubview:self.connectButton]; 106 | [self.connectButton addTarget:self action:@selector(doConnect:) forControlEvents:UIControlEventTouchUpInside]; 107 | [self.connectButton addTarget:self.connectButton action:@selector(touchDown) forControlEvents:UIControlEventTouchDown]; 108 | [self.connectButton addTarget:self.connectButton action:@selector(touchUpInside) forControlEvents:UIControlEventTouchUpInside]; 109 | [self.connectButton addTarget:self.connectButton action:@selector(touchUpInside) forControlEvents:UIControlEventTouchDragOutside]; 110 | 111 | [self.connectButton setTitle:NSLocalizedString(@"Connect", nil) forState:UIControlStateNormal]; 112 | self.connectButton.alpha = 0; 113 | 114 | self.tableViewTopConstraint.constant = TableViewTopConstraintInitialConstant; 115 | self.stationsTableVIew.tableHeaderView = headerView; 116 | self.stationsTableVIew.delegate = self; 117 | self.stationsTableVIew.dataSource = self; 118 | [self.stationsTableVIew reloadData]; 119 | 120 | [[NSNotificationCenter defaultCenter] addObserver:self 121 | selector:@selector(filterStationStatus:) 122 | name:CCNFilterStationStatus 123 | object:nil]; 124 | 125 | [[NSNotificationCenter defaultCenter] addObserver:self 126 | selector:@selector(fillUserInfo) 127 | name:CCNFillUserInfo 128 | object:nil]; 129 | 130 | self.vpnmanager = [[VCIPsecVPNManager alloc] init]; 131 | [self addMotionOnMap]; 132 | 133 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnConnectionStatusChanged:) name:NEVPNStatusDidChangeNotification object:nil]; 134 | 135 | 136 | if (![GVUserDefaults standardUserDefaults].token) { 137 | LoginViewController * loginViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"LoginViewController"]; 138 | loginViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext; 139 | 140 | [self presentViewController:loginViewController animated:NO completion:^{ 141 | 142 | }]; 143 | }else{ 144 | NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 145 | 146 | [sharedDefaults setBool:YES forKey:@"ActiveToday"]; 147 | [sharedDefaults synchronize]; // (!!) This is crucial. 148 | [self checkCanInvite]; 149 | [[NSNotificationCenter defaultCenter] postNotificationName:CCNFillUserInfo object:nil]; 150 | [self validateUser]; 151 | } 152 | 153 | [self.view bringSubviewToFront:self.stationsTableVIew]; 154 | [self.view bringSubviewToFront:self.connectButton]; 155 | 156 | 157 | } 158 | 159 | -(BOOL)canBecomeFirstResponder { 160 | return YES; 161 | } 162 | 163 | -(void)viewDidAppear:(BOOL)animated { 164 | [super viewDidAppear:animated]; 165 | [self becomeFirstResponder]; 166 | } 167 | 168 | - (void)viewWillDisappear:(BOOL)animated { 169 | [self resignFirstResponder]; 170 | [super viewWillDisappear:animated]; 171 | } 172 | 173 | - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event 174 | { 175 | if (motion == UIEventSubtypeMotionShake) 176 | { 177 | NSLog(@"Change IKEV"); 178 | 179 | NSString * message = @"Choose The Encrypt Protocal"; 180 | 181 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Make A Change" message:message preferredStyle:UIAlertControllerStyleAlert]; 182 | 183 | UIAlertAction* ikev2 = [UIAlertAction actionWithTitle:@"IKEv2" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 184 | [GVUserDefaults standardUserDefaults].ikev2 = YES; 185 | }]; 186 | 187 | UIAlertAction* ikev1 = [UIAlertAction actionWithTitle:@"IKEv1" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 188 | [GVUserDefaults standardUserDefaults].ikev2 = NO; 189 | }]; 190 | 191 | 192 | NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 193 | 194 | [sharedDefaults setBool:[GVUserDefaults standardUserDefaults].ikev2 forKey:@"ikev2"]; 195 | [sharedDefaults synchronize]; // (!!) This is crucial. 196 | 197 | [alertController addAction:ikev2]; 198 | [alertController addAction:ikev1]; 199 | 200 | [self presentViewController:alertController animated:YES completion:nil]; 201 | } 202 | } 203 | 204 | -(void)viewWillAppear:(BOOL)animated 205 | { 206 | [super viewWillAppear:animated]; 207 | if (self.userHasLogined) { 208 | [self relayout]; 209 | [self checkCanInvite]; 210 | } 211 | 212 | } 213 | 214 | -(void)checkCanInvite 215 | { 216 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 217 | NSDictionary *parameters = @{ 218 | @"user_token":[GVUserDefaults standardUserDefaults].token 219 | }; 220 | [manager POST:[NSString stringWithFormat:@"%@%@",[[VPNStations sharedInstance].config valueForKey:@"server"], [[VPNStations sharedInstance].config valueForKey:@"can_do_invite"]] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { 221 | 222 | #ifdef DEBUG 223 | NSLog(@"JSON: %@", responseObject); 224 | #endif 225 | 226 | NSString * status = [responseObject valueForKey:@"status"]; 227 | 228 | if([status isEqualToString:@"error"]){ 229 | self.userCanInvite = NO; 230 | }else{ 231 | self.userCanInvite = YES; 232 | } 233 | 234 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 235 | 236 | #ifdef DEBUG 237 | NSLog(@"Error: %@", error); 238 | #endif 239 | self.userCanInvite = NO; 240 | }]; 241 | 242 | 243 | } 244 | 245 | -(void)addMotionOnMap 246 | { 247 | // Set vertical effect 248 | UIInterpolatingMotionEffect *verticalMotionEffect = 249 | [[UIInterpolatingMotionEffect alloc] 250 | initWithKeyPath:@"center.y" 251 | type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; 252 | verticalMotionEffect.minimumRelativeValue = @(-20); 253 | verticalMotionEffect.maximumRelativeValue = @(20); 254 | 255 | // Set horizontal effect 256 | UIInterpolatingMotionEffect *horizontalMotionEffect = 257 | [[UIInterpolatingMotionEffect alloc] 258 | initWithKeyPath:@"center.x" 259 | type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; 260 | horizontalMotionEffect.minimumRelativeValue = @(-20); 261 | horizontalMotionEffect.maximumRelativeValue = @(20); 262 | 263 | // Create group to combine both 264 | UIMotionEffectGroup *group = [UIMotionEffectGroup new]; 265 | group.motionEffects = @[horizontalMotionEffect, verticalMotionEffect]; 266 | 267 | // Add both effects to your view 268 | [self.mapImageVIew addMotionEffect:group]; 269 | } 270 | 271 | -(void)vpnConnectionStatusChanged:(NSNotification *)notification 272 | { 273 | [self relayout]; 274 | } 275 | 276 | -(void)filterStationStatus:(NSNotification *)notification 277 | { 278 | NSDictionary * dict = notification.object; 279 | self.currentStationDict = dict; 280 | self.domain = [dict valueForKey:@"host"]; 281 | 282 | if (![[[GVUserDefaults standardUserDefaults].currentStationDict valueForKey:@"host"] isEqualToString:[dict valueForKey:@"host"]]) { 283 | [GVUserDefaults standardUserDefaults].server = self.domain; 284 | [GVUserDefaults standardUserDefaults].currentStationDict = dict; 285 | } 286 | 287 | [self fillWithStationView:self.stationViewOne]; 288 | 289 | [self changeLocation:CGPointMake([[dict valueForKey:@"x"] floatValue], [[dict valueForKey:@"y"] floatValue])]; 290 | 291 | #ifdef DEBUG 292 | NSLog(@"Change domian to %@",[GVUserDefaults standardUserDefaults].currentStationDict ); 293 | #endif 294 | 295 | } 296 | 297 | 298 | -(void)changeLocation:(CGPoint)point 299 | { 300 | 301 | 302 | #ifdef DEBUG 303 | NSLog(@"Change station to %f %f",point.x, point.y ); 304 | #endif 305 | 306 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 307 | animAlpha.toValue = @0.0; 308 | animAlpha.springBounciness = 4.0; 309 | animAlpha.springSpeed = 12.0; 310 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 311 | if (finished) {}}; 312 | [self.stationLocationView.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 313 | 314 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 315 | if (finished) { 316 | 317 | self.stationLocationView.center = CGPointMake((self.mapImageVIew.frame.size.width /0.9) /2.0 + ((self.mapImageVIew.frame.size.width /0.9) /2.0 * point.x) - 15.0, (self.mapImageVIew.frame.size.height/0.9) /2.0 + ((self.mapImageVIew.frame.size.height/0.9) /2.0 * point.y) -15.0); 318 | 319 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 320 | animAlpha.toValue = @1.0; 321 | animAlpha.springBounciness = 4.0; 322 | animAlpha.springSpeed = 12.0; 323 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 324 | if (finished) {}}; 325 | [self.stationLocationView.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 326 | 327 | 328 | 329 | } 330 | }; 331 | 332 | } 333 | 334 | 335 | 336 | 337 | - (void)fillUserInfo 338 | { 339 | self.userHasLogined = YES; 340 | 341 | #ifdef DEBUG 342 | NSLog(@"User Do login"); 343 | #endif 344 | self.mapImageVIew.image = [UIImage imageNamed:@"World Map"]; 345 | 346 | POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 347 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/2.0, (SCREEN_HEIGHT - 240.0) )]; 348 | anim.springBounciness = 4.0; 349 | anim.springSpeed = 12.0; 350 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 351 | if (finished) {}}; 352 | [self.mapImageVIew.layer pop_addAnimation:anim forKey:@"MoveMap"]; 353 | 354 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 355 | animAlpha.toValue = @1.0; 356 | animAlpha.springBounciness = 4.0; 357 | animAlpha.springSpeed = 12.0; 358 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 359 | if (finished) {}}; 360 | [self.mapImageVIew.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 361 | 362 | POPSpringAnimation *animSize = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 363 | animSize.toValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)]; 364 | animSize.springBounciness = 15.0; 365 | animSize.springSpeed = 12.0; 366 | animSize.completionBlock = ^(POPAnimation *anim, BOOL finished) { 367 | if (finished) { 368 | 369 | #ifdef DEBUG 370 | NSLog(@"Update station %@", [GVUserDefaults standardUserDefaults].currentStationDict); 371 | #endif 372 | 373 | if ([GVUserDefaults standardUserDefaults].currentStationDict) { 374 | self.currentStationDict = [GVUserDefaults standardUserDefaults].currentStationDict; 375 | [self changeLocation:CGPointMake([[self.currentStationDict valueForKey:@"x"] floatValue], [[self.currentStationDict valueForKey:@"y"] floatValue])]; 376 | [[NSNotificationCenter defaultCenter] postNotificationName:CCNFilterStationStatus object:self.currentStationDict]; 377 | }else{ 378 | [self changeLocation:CGPointMake(0, 0)]; 379 | } 380 | 381 | [self.stationLocationView show]; 382 | 383 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 384 | animAlpha.toValue = @1.0; 385 | animAlpha.springBounciness = 4.0; 386 | animAlpha.springSpeed = 12.0; 387 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 388 | if (finished) {}}; 389 | [self.connectButton.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 390 | 391 | POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 392 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/2.0, (SCREEN_HEIGHT - 70.0) )]; 393 | anim.springBounciness = 10.0; 394 | anim.springSpeed = 12.0; 395 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 396 | if (finished) { 397 | 398 | [self relayout]; 399 | 400 | dispatch_async(dispatch_get_main_queue(), ^{ 401 | 402 | [UIView animateWithDuration:0.3 animations:^{ 403 | 404 | NSString * sponser = [self.stationDic valueForKey:@"sponser"]; 405 | 406 | NSString * name = [self.stationDic valueForKey:@"name"]; 407 | 408 | if (![sponser isEqualToString:@""]) { 409 | self.donaterLabel.text = [NSString stringWithFormat:@"%@ sponsored by %@", name, sponser]; 410 | self.donaterLabel.alpha = 1.0; 411 | } else { 412 | self.donaterLabel.alpha = 0.0; 413 | } 414 | 415 | 416 | }]; 417 | 418 | }); 419 | 420 | 421 | 422 | } 423 | }; 424 | [self.connectButton.layer pop_addAnimation:anim forKey:@"MoveMap"]; 425 | 426 | 427 | [self showStations]; 428 | 429 | } 430 | }; 431 | [self.mapImageVIew.layer pop_addAnimation:animSize forKey:@"SizeMap"]; 432 | } 433 | 434 | - (void)didReceiveMemoryWarning { 435 | [super didReceiveMemoryWarning]; 436 | // Dispose of any resources that can be recreated. 437 | } 438 | 439 | - (IBAction)doConnect:(id)sender { 440 | 441 | 442 | // [self bumbButton]; 443 | if (self.isPrepareProfile) { 444 | return; 445 | } 446 | self.isPrepareProfile = YES; 447 | [self.vpnmanager prepareWithCompletion:^(NSError *error) { 448 | self.isPrepareProfile = NO; 449 | 450 | if (self.vpnmanager.vpnManager.connection.status != NEVPNStatusDisconnected && self.vpnmanager.vpnManager.connection.status != NEVPNStatusInvalid) { 451 | 452 | [self.vpnmanager.vpnManager.connection stopVPNTunnel]; 453 | 454 | [self showStations]; 455 | 456 | NSLog(@"Show stations"); 457 | 458 | }else{ 459 | if (self.domain) { 460 | 461 | [self hideStations]; 462 | 463 | 464 | if ([GVUserDefaults standardUserDefaults].ikev2) { 465 | [self.vpnmanager connectIPSecIKEv2WithHost:self.domain andUsername:[[VPNStations sharedInstance].config valueForKey:@"username"] andPassword:[[VPNStations sharedInstance].config valueForKey:@"password"] andPSK:[[VPNStations sharedInstance].config valueForKey:@"psk"] andGroupName:[[VPNStations sharedInstance].config valueForKey:@"groupname"]]; 466 | } else { 467 | [self.vpnmanager connectIPSecWithHost:self.domain andUsername:[[VPNStations sharedInstance].config valueForKey:@"username"] andPassword:[[VPNStations sharedInstance].config valueForKey:@"password"] andPSK:[[VPNStations sharedInstance].config valueForKey:@"psk"] andGroupName:[[VPNStations sharedInstance].config valueForKey:@"groupname"]]; 468 | } 469 | 470 | } 471 | } 472 | 473 | }]; 474 | 475 | 476 | } 477 | 478 | -(void)showStationView 479 | { 480 | [self fillWithStationView:self.stationViewOne]; 481 | 482 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 483 | animAlpha.toValue = @1.0; 484 | animAlpha.springBounciness = 4.0; 485 | animAlpha.springSpeed = 12.0; 486 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 487 | if (finished) {}}; 488 | [self.stationViewOne.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 489 | 490 | POPSpringAnimation *anim = 491 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 492 | anim.springBounciness = 10; 493 | anim.springSpeed = 20; 494 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0.5, 0.5)]; 495 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 496 | [self.stationViewOne.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 497 | 498 | self.stationViewOne.displayed = YES; 499 | 500 | } 501 | 502 | -(void)hideStationView 503 | { 504 | 505 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 506 | animAlpha.toValue = @0.0; 507 | animAlpha.springBounciness = 4.0; 508 | animAlpha.springSpeed = 12.0; 509 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 510 | if (finished) {}}; 511 | [self.stationViewOne.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 512 | 513 | POPSpringAnimation *anim = 514 | [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 515 | anim.springBounciness = 10; 516 | anim.springSpeed = 20; 517 | anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 1.0)]; 518 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(0.5, 0.5)]; 519 | [self.stationViewOne.layer pop_addAnimation:anim forKey:@"AnimationScaleBack"]; 520 | 521 | self.stationViewOne.displayed = NO; 522 | 523 | } 524 | 525 | - (void)hideTableView 526 | { 527 | 528 | #ifdef DEBUG 529 | NSLog(@"Hide table view"); 530 | #endif 531 | [self.tableViewTopConstraint pop_removeAnimationForKey:@"stationsHideAnimation"]; 532 | POPSpringAnimation *stationsHideAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayoutConstraintConstant]; 533 | stationsHideAnimation.toValue = @(TableViewTopConstraintInitialConstant); 534 | stationsHideAnimation.springSpeed = 4.0; 535 | stationsHideAnimation.springBounciness = 0.0; 536 | stationsHideAnimation.removedOnCompletion = YES; 537 | [self.tableViewTopConstraint pop_addAnimation:stationsHideAnimation forKey:@"stationsHideAnimation"]; 538 | 539 | [self.tableViewTopConstraint pop_removeAnimationForKey:@"AlphaMap"]; 540 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 541 | animAlpha.toValue = @0.0; 542 | animAlpha.springBounciness = 0.0; 543 | animAlpha.springSpeed = 20.0; 544 | animAlpha.completionBlock = ^(POPAnimation *anim, BOOL finished) { 545 | if (finished) {}}; 546 | animAlpha.removedOnCompletion = YES; 547 | [self.stationsTableVIew.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 548 | } 549 | 550 | -(void)hideStations 551 | { 552 | [self hideTableView]; 553 | 554 | [self showStationView]; 555 | 556 | self.stationShowed = NO; 557 | } 558 | 559 | - (void)showTableView 560 | { 561 | 562 | POPSpringAnimation *stationsShowAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayoutConstraintConstant]; 563 | stationsShowAnimation.toValue = @0; 564 | stationsShowAnimation.springSpeed = 6.0; 565 | stationsShowAnimation.springBounciness = 10.0; 566 | stationsShowAnimation.removedOnCompletion = YES; 567 | [self.tableViewTopConstraint pop_addAnimation:stationsShowAnimation forKey:@"stationsShowAnimation"]; 568 | 569 | POPSpringAnimation *animAlpha = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 570 | animAlpha.toValue = @1.0; 571 | animAlpha.springBounciness = 4.0; 572 | animAlpha.springSpeed = 12.0; 573 | animAlpha.removedOnCompletion = YES; 574 | [self.stationsTableVIew.layer pop_addAnimation:animAlpha forKey:@"AlphaMap"]; 575 | } 576 | 577 | -(void)showStations 578 | { 579 | [self showTableView]; 580 | 581 | [self hideStationView]; 582 | 583 | self.stationShowed = YES; 584 | } 585 | 586 | -(void)relayout 587 | { 588 | 589 | [self.vpnmanager prepareWithCompletion:^(NSError *error) { 590 | 591 | if (self.vpnmanager.vpnManager.connection.status == NEVPNStatusConnected) { 592 | 593 | 594 | [UIView transitionWithView:self.mapImageVIew 595 | duration:0.3 596 | options:UIViewAnimationOptionTransitionCrossDissolve 597 | animations:^{ 598 | self.mapImageVIew.image = [UIImage imageNamed:@"Map"]; 599 | } 600 | completion:NULL]; 601 | self.stationViewOne.status = @"Connected"; 602 | // [self activeButtonStatus:self.connectionButton]; 603 | 604 | [self.connectButton setTitle:NSLocalizedString(@"Disconnect", nil) forState:UIControlStateNormal]; 605 | [self.connectButton setBackgroundImage:[UIImage imageNamed:@"Button_wire"] forState:UIControlStateNormal]; 606 | 607 | if (!self.stationViewOne.displayed) { 608 | [self hideStations]; 609 | } 610 | 611 | }else if (self.vpnmanager.vpnManager.connection.status == NEVPNStatusConnecting) { 612 | 613 | // [self activeButtonStatus:self.connectionButton]; 614 | 615 | self.stationViewOne.status = @"Connecting"; 616 | [self.connectButton setTitle:NSLocalizedString(@"Connecting", nil) forState:UIControlStateNormal]; 617 | [self.connectButton setBackgroundImage:[UIImage imageNamed:@"Button_wire"] forState:UIControlStateNormal]; 618 | 619 | } 620 | else if ( self.vpnmanager.vpnManager.connection.status == NEVPNStatusDisconnecting) 621 | { 622 | 623 | // [self.connectButton setTitle:@"Disconnection" forState:UIControlStateNormal]; 624 | } 625 | else if(self.vpnmanager.vpnManager.connection.status == NEVPNStatusDisconnected){ 626 | 627 | [UIView transitionWithView:self.mapImageVIew 628 | duration:0.3 629 | options:UIViewAnimationOptionTransitionCrossDissolve 630 | animations:^{ 631 | self.mapImageVIew.image = [UIImage imageNamed:@"World Map"]; 632 | } 633 | completion:NULL]; 634 | 635 | [self.connectButton setTitle:NSLocalizedString(@"Connect", nil) forState:UIControlStateNormal]; 636 | [self.connectButton setBackgroundImage:[UIImage imageNamed:@"Button + Connect"] forState:UIControlStateNormal]; 637 | if (![self.stationViewOne.status isEqualToString:@"Disconnected"] && !self.stationShowed) { 638 | [SVProgressHUD showInfoWithStatus:@"Connect Failed"]; 639 | [self showStations]; 640 | } 641 | 642 | self.stationViewOne.status = @"Disconnected"; 643 | // [self inactiveButtonStatus:self.connectionButton]; 644 | } 645 | 646 | 647 | }]; 648 | } 649 | 650 | #pragma mark - Table View 651 | 652 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 653 | { 654 | return 1; 655 | } 656 | 657 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 658 | return self.vpnStations.count; 659 | } 660 | 661 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 662 | { 663 | StationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"stationCell"]; 664 | if (cell == nil) { 665 | cell = [[StationTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"stationCell"]; 666 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 667 | } 668 | 669 | NSDictionary * station = [self.vpnStations objectAtIndex:indexPath.row]; 670 | 671 | cell.flagImageView.image = [UIImage imageNamed:[station valueForKey:@"short_name"]]; 672 | cell.stationNameLabel.text =[station valueForKey:@"name"]; 673 | [cell setDomain:[station valueForKey:@"host"] withIndex:indexPath.row]; 674 | if([cell.domain isEqualToString:[GVUserDefaults standardUserDefaults].server]){ 675 | self.stationDic = [self.vpnStations objectAtIndex:indexPath.row]; 676 | } 677 | 678 | cell.stationDic = station; 679 | 680 | #ifdef DEBUG 681 | NSLog(@"Show cell"); 682 | #endif 683 | 684 | return cell; 685 | } 686 | 687 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 688 | { 689 | 690 | 691 | #ifdef DEBUG 692 | NSLog(@"Did select row"); 693 | #endif 694 | 695 | StationTableViewCell *cell = (StationTableViewCell *)[tableView cellForRowAtIndexPath:indexPath]; 696 | [cell makeCheck]; 697 | 698 | self.stationDic = [self.vpnStations objectAtIndex:indexPath.row]; 699 | 700 | [UIView animateWithDuration:0.3 animations:^{ 701 | 702 | NSString * sponser = [self.stationDic valueForKey:@"sponser"]; 703 | 704 | NSString * name = [self.stationDic valueForKey:@"name"]; 705 | 706 | if (![sponser isEqualToString:@""]) { 707 | self.donaterLabel.text = [NSString stringWithFormat:@"%@ sponsored by %@", name, sponser]; 708 | self.donaterLabel.alpha = 1.0; 709 | } else { 710 | self.donaterLabel.alpha = 0.0; 711 | } 712 | 713 | 714 | 715 | }]; 716 | } 717 | 718 | 719 | -(void)fillWithStationView:(stationView *)view 720 | { 721 | view.stationFlag.image = [UIImage imageNamed:[self.stationDic valueForKey:@"short_name"]]; 722 | view.name = [self.stationDic valueForKey:@"name"]; 723 | view.status = @"Connecting"; 724 | } 725 | 726 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 727 | { 728 | 729 | #ifdef DEBUG 730 | NSLog(@"scrollView.contentOffset.y: %@", @(scrollView.contentOffset.y)); 731 | #endif 732 | 733 | [self.stationsTableVIew.layer pop_removeAnimationForKey:@"stationsShowAnimation"]; 734 | 735 | CGFloat thresholdForShowInviteView = 70.0; 736 | 737 | if (self.inviteView) { 738 | // scrollView.alpha = 0.0; 739 | } else if (self.userCanInvite){ 740 | scrollView.alpha = 1.0 - (MIN(thresholdForShowInviteView, scrollView.contentOffset.y) / thresholdForShowInviteView); 741 | } 742 | 743 | if (scrollView.contentOffset.y > thresholdForShowInviteView) { 744 | 745 | if (!self.inviteView && self.userCanInvite) { 746 | //scrollView.hidden = YES; 747 | scrollView.scrollEnabled = NO; 748 | //CGPoint contentOffset = scrollView.contentOffset; 749 | //contentOffset.y = thresholdForShowInviteView; 750 | //scrollView.contentOffset = contentOffset; 751 | scrollView.alpha = 0.0; 752 | //scrollView.hidden = NO; 753 | //NSLog(@"do show invite view"); 754 | [self showInviteView]; 755 | } 756 | } 757 | } 758 | 759 | #pragma mark - Invite View 760 | 761 | - (InviteView *)makeInviteView 762 | { 763 | InviteView *inviteView = [[InviteView alloc] initWithFrame:CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT)]; 764 | //inviteView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2]; 765 | 766 | [self.view addSubview:inviteView]; 767 | 768 | return inviteView; 769 | } 770 | 771 | - (BOOL)textFieldShouldReturn:(UITextField *)textField 772 | { 773 | [self invite]; 774 | 775 | return YES; 776 | } 777 | 778 | - (void)invite 779 | { 780 | 781 | #ifdef DEBUG 782 | NSLog(@"do invite"); 783 | #endif 784 | if (!self.inviteView.emailTextField.text.length > 1) { 785 | return; 786 | } 787 | 788 | [SVProgressHUD show]; 789 | [self doInviteEmail:self.inviteView.emailTextField.text]; 790 | [self hideInviteView]; 791 | } 792 | 793 | - (void)showInviteView 794 | { 795 | self.inviteView = [self makeInviteView]; 796 | 797 | [self hideTableView]; 798 | 799 | POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 800 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH * 0.5, (SCREEN_WIDTH * 0.3))]; 801 | anim.springBounciness = 4.0; 802 | anim.springSpeed = 12.0; 803 | anim.removedOnCompletion = YES; 804 | [self.mapImageVIew.layer pop_addAnimation:anim forKey:@"MoveMap"]; 805 | 806 | POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 807 | scaleAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.2, 1.2)]; 808 | scaleAnimation.removedOnCompletion = YES; 809 | [self.mapImageVIew.layer pop_addAnimation:scaleAnimation forKey:@"Scale"]; 810 | 811 | 812 | POPSpringAnimation *showInviteViewAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 813 | showInviteViewAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH * 0.5, (SCREEN_HEIGHT * 0.5))]; 814 | showInviteViewAnimation.springBounciness = 4.0; 815 | showInviteViewAnimation.springSpeed = 12.0; 816 | showInviteViewAnimation.removedOnCompletion = YES; 817 | showInviteViewAnimation.completionBlock = ^(POPAnimation *anim, BOOL finished) { 818 | if (finished) { 819 | [_inviteView.emailTextField becomeFirstResponder]; 820 | _inviteView.emailTextField.delegate = self; 821 | 822 | [_inviteView.inviteButton addTarget:self action:@selector(invite) forControlEvents:UIControlEventTouchUpInside]; 823 | 824 | UISwipeGestureRecognizer *swipeDownGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(hideInviteView)]; 825 | swipeDownGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown; 826 | [_inviteView addGestureRecognizer:swipeDownGestureRecognizer]; 827 | } 828 | }; 829 | 830 | [_inviteView.layer pop_addAnimation:showInviteViewAnimation forKey:@"showInviteViewAnimation"]; 831 | } 832 | 833 | - (void)hideInviteView 834 | { 835 | [self.inviteView.emailTextField resignFirstResponder]; 836 | 837 | self.stationsTableVIew.scrollEnabled = YES; 838 | 839 | POPSpringAnimation *hideInviteViewAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 840 | hideInviteViewAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH * 0.5, (SCREEN_HEIGHT * 1.5))]; 841 | hideInviteViewAnimation.springBounciness = 4.0; 842 | hideInviteViewAnimation.springSpeed = 8.0; 843 | hideInviteViewAnimation.removedOnCompletion = YES; 844 | 845 | [_inviteView.layer pop_addAnimation:hideInviteViewAnimation forKey:@"hideInviteViewAnimation"]; 846 | 847 | POPBasicAnimation *alphaAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 848 | alphaAnimation.toValue = @0; 849 | alphaAnimation.duration = 0.2; 850 | alphaAnimation.removedOnCompletion = YES; 851 | [self.inviteView.layer pop_addAnimation:alphaAnimation forKey:@"alphaAnimation"]; 852 | 853 | POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; 854 | anim.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH * 0.5, (SCREEN_HEIGHT - 240.0))]; 855 | anim.springBounciness = 4.0; 856 | anim.springSpeed = 8.0; 857 | anim.removedOnCompletion = YES; 858 | anim.completionBlock = ^(POPAnimation *anim, BOOL finished) { 859 | if (finished) { 860 | [_inviteView removeFromSuperview]; 861 | } 862 | }; 863 | [self.mapImageVIew.layer pop_addAnimation:anim forKey:@"MoveMap"]; 864 | 865 | POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 866 | scaleAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)]; 867 | scaleAnimation.removedOnCompletion = YES; 868 | [self.mapImageVIew.layer pop_addAnimation:scaleAnimation forKey:@"Scale"]; 869 | 870 | [self showTableView]; 871 | } 872 | 873 | -(void)doInviteEmail:(NSString *)email 874 | { 875 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 876 | NSDictionary *parameters = @{ 877 | @"email": email, 878 | @"user_token":[GVUserDefaults standardUserDefaults].token 879 | }; 880 | [manager POST:[NSString stringWithFormat:@"%@%@",[[VPNStations sharedInstance].config valueForKey:@"server"], [[VPNStations sharedInstance].config valueForKey:@"do_invite"]] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { 881 | 882 | #ifdef DEBUG 883 | NSLog(@"JSON: %@", responseObject); 884 | #endif 885 | 886 | NSString * message = [responseObject valueForKey:@"message"]; 887 | NSString * status = [responseObject valueForKey:@"status"]; 888 | if([status isEqualToString:@"error"]){ 889 | [SVProgressHUD showErrorWithStatus:message]; 890 | }else{ 891 | [SVProgressHUD showSuccessWithStatus:message]; 892 | } 893 | 894 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 895 | 896 | #ifdef DEBUG 897 | NSLog(@"Error: %@", error); 898 | #endif 899 | 900 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 901 | // time-consuming task 902 | dispatch_async(dispatch_get_main_queue(), ^{ 903 | [SVProgressHUD showErrorWithStatus:[error description]]; 904 | }); 905 | }); 906 | }]; 907 | 908 | } 909 | 910 | - (void)validateUser 911 | { 912 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 913 | NSDictionary *parameters = @{ 914 | @"user_token":[GVUserDefaults standardUserDefaults].token 915 | }; 916 | [manager POST:[NSString stringWithFormat:@"%@%@",[[VPNStations sharedInstance].config valueForKey:@"server"], [[VPNStations sharedInstance].config valueForKey:@"validate_user"]] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { 917 | 918 | #ifdef DEBUG 919 | NSLog(@"JSON: %@", responseObject); 920 | #endif 921 | 922 | NSString * status = [responseObject valueForKey:@"status"]; 923 | if([status isEqualToString:@"error"]){ 924 | 925 | [GVUserDefaults standardUserDefaults].token = nil; 926 | NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.catchlab.TodayExtensionSharingDefaults"]; 927 | 928 | [sharedDefaults setBool:NO forKey:@"ActiveToday"]; 929 | [sharedDefaults synchronize]; // (!!) This is crucial. 930 | } 931 | 932 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 933 | 934 | #ifdef DEBUG 935 | NSLog(@"Error: %@", error); 936 | #endif 937 | }]; 938 | } 939 | 940 | @end 941 | --------------------------------------------------------------------------------