From e8c8f4f8e9368b172bd9e3b5fbf9fbab8c18f521 Mon Sep 17 00:00:00 2001 From: Diego Waxemberg Date: Thu, 1 Jan 2015 22:30:03 -0500 Subject: [PATCH] Created new host discovery system - now store host uuid and mac address - use uuid to check for duplicate hosts - try local and external IPs - pair status is shown - server status is shown - validate host when manually adding --- Limelight.xcodeproj/project.pbxproj | 12 +- Limelight/Database/DataManager.h | 2 +- Limelight/Database/DataManager.m | 5 +- Limelight/Database/Host.h | 10 +- Limelight/Database/Host.m | 8 +- .../Limelight 0.3.0.xcdatamodel/contents | 7 +- Limelight/Network/DiscoveryManager.h | 27 ++++ Limelight/Network/DiscoveryManager.m | 149 ++++++++++++++++++ Limelight/Network/MDNSManager.m | 8 +- Limelight/UIComputerView.h | 8 +- Limelight/UIComputerView.m | 58 +++++-- Limelight/Utility/Computer.h | 19 --- Limelight/Utility/Computer.m | 47 ------ Limelight/Utility/Utils.h | 8 + Limelight/Utility/Utils.m | 1 + .../ViewControllers/MainFrameViewController.h | 4 +- .../ViewControllers/MainFrameViewController.m | 107 +++++++------ 17 files changed, 326 insertions(+), 154 deletions(-) create mode 100644 Limelight/Network/DiscoveryManager.h create mode 100644 Limelight/Network/DiscoveryManager.m delete mode 100644 Limelight/Utility/Computer.h delete mode 100644 Limelight/Utility/Computer.m diff --git a/Limelight.xcodeproj/project.pbxproj b/Limelight.xcodeproj/project.pbxproj index f5a12b05..f7647f53 100644 --- a/Limelight.xcodeproj/project.pbxproj +++ b/Limelight.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ FB290E7919B37D81004C83CF /* iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FB290E7819B37D81004C83CF /* iPad.storyboard */; }; FB290E7B19B38036004C83CF /* iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FB290E7A19B38036004C83CF /* iPhone.storyboard */; }; FB4678ED1A50C40900377732 /* OnScreenControls.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678EC1A50C40900377732 /* OnScreenControls.m */; }; + FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678F91A55FFAD00377732 /* DiscoveryManager.m */; }; FB7E794419C8B71B00A15F68 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB7E794319C8B71B00A15F68 /* libiconv.dylib */; }; FB89462819F646E200339C8A /* CryptoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89460619F646E200339C8A /* CryptoManager.m */; }; FB89462919F646E200339C8A /* mkcert.c in Sources */ = {isa = PBXBuildFile; fileRef = FB89460719F646E200339C8A /* mkcert.c */; }; @@ -40,7 +41,6 @@ FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89461919F646E200339C8A /* StreamConfiguration.m */; }; FB89463119F646E200339C8A /* StreamManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89461B19F646E200339C8A /* StreamManager.m */; }; FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89461D19F646E200339C8A /* VideoDecoderRenderer.m */; }; - FB89463319F646E200339C8A /* Computer.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462019F646E200339C8A /* Computer.m */; }; FB89463419F646E200339C8A /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462219F646E200339C8A /* Utils.m */; }; FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462519F646E200339C8A /* MainFrameViewController.m */; }; FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462719F646E200339C8A /* StreamFrameViewController.m */; }; @@ -110,6 +110,8 @@ FB4678EB1A50C40900377732 /* OnScreenControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OnScreenControls.h; sourceTree = ""; }; FB4678EC1A50C40900377732 /* OnScreenControls.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnScreenControls.m; sourceTree = ""; }; FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Limelight 0.3.0.xcdatamodel"; sourceTree = ""; }; + FB4678F81A55FFAD00377732 /* DiscoveryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiscoveryManager.h; sourceTree = ""; }; + FB4678F91A55FFAD00377732 /* DiscoveryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiscoveryManager.m; sourceTree = ""; }; FB7E794319C8B71B00A15F68 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; FB89460519F646E200339C8A /* CryptoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoManager.h; sourceTree = ""; }; FB89460619F646E200339C8A /* CryptoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoManager.m; sourceTree = ""; }; @@ -133,8 +135,6 @@ FB89461B19F646E200339C8A /* StreamManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamManager.m; sourceTree = ""; }; FB89461C19F646E200339C8A /* VideoDecoderRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoDecoderRenderer.h; sourceTree = ""; }; FB89461D19F646E200339C8A /* VideoDecoderRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoDecoderRenderer.m; sourceTree = ""; }; - FB89461F19F646E200339C8A /* Computer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Computer.h; sourceTree = ""; }; - FB89462019F646E200339C8A /* Computer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Computer.m; sourceTree = ""; }; FB89462119F646E200339C8A /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; FB89462219F646E200339C8A /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; FB89462419F646E200339C8A /* MainFrameViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainFrameViewController.h; sourceTree = ""; }; @@ -413,6 +413,8 @@ FB89461419F646E200339C8A /* PairManager.m */, FBD3494119FC9C04002D2A60 /* AppManager.h */, FBD3494219FC9C04002D2A60 /* AppManager.m */, + FB4678F81A55FFAD00377732 /* DiscoveryManager.h */, + FB4678F91A55FFAD00377732 /* DiscoveryManager.m */, ); path = Network; sourceTree = ""; @@ -435,8 +437,6 @@ FB89461E19F646E200339C8A /* Utility */ = { isa = PBXGroup; children = ( - FB89461F19F646E200339C8A /* Computer.h */, - FB89462019F646E200339C8A /* Computer.m */, FBDE86E719F82315001C18A8 /* App.h */, FBDE86E819F82315001C18A8 /* App.m */, FB89462119F646E200339C8A /* Utils.h */, @@ -758,7 +758,6 @@ FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */, FB89463419F646E200339C8A /* Utils.m in Sources */, FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */, - FB89463319F646E200339C8A /* Computer.m in Sources */, FB89462F19F646E200339C8A /* Connection.m in Sources */, FB89462919F646E200339C8A /* mkcert.c in Sources */, FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */, @@ -769,6 +768,7 @@ FB89462C19F646E200339C8A /* HttpManager.m in Sources */, FB89462D19F646E200339C8A /* MDNSManager.m in Sources */, FB89462B19F646E200339C8A /* StreamView.m in Sources */, + FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */, FBD3495B1A004411002D2A60 /* Host.m in Sources */, FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */, FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */, diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h index 3df772b2..fc4e005f 100644 --- a/Limelight/Database/DataManager.h +++ b/Limelight/Database/DataManager.h @@ -19,6 +19,6 @@ - (Settings*) retrieveSettings; - (NSArray*) retrieveHosts; - (void) saveHosts; -- (Host*) createHost:(NSString*)name hostname:(NSString*)address; +- (Host*) createHost; @end diff --git a/Limelight/Database/DataManager.m b/Limelight/Database/DataManager.m index 69daf7ab..39ec1969 100644 --- a/Limelight/Database/DataManager.m +++ b/Limelight/Database/DataManager.m @@ -45,12 +45,9 @@ } } -- (Host*) createHost:(NSString*)name hostname:(NSString*)address { +- (Host*) createHost { NSEntityDescription* entity = [NSEntityDescription entityForName:@"Host" inManagedObjectContext:[self.appDelegate managedObjectContext]]; Host* host = [[Host alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]]; - - host.name = name; - host.address = address; return host; } diff --git a/Limelight/Database/Host.h b/Limelight/Database/Host.h index c63b4c4c..565ffcb5 100644 --- a/Limelight/Database/Host.h +++ b/Limelight/Database/Host.h @@ -8,11 +8,17 @@ #import #import - +#import "Utils.h" @interface Host : NSManagedObject -@property (nonatomic, retain) NSString * address; @property (nonatomic, retain) NSString * name; +@property (nonatomic, retain) NSString * address; +@property (nonatomic, retain) NSString * localAddress; +@property (nonatomic, retain) NSString * externalAddress; +@property (nonatomic, retain) NSString * uuid; +@property (nonatomic, retain) NSString * mac; +@property (nonatomic) BOOL online; +@property (nonatomic) PairState pairState; @end diff --git a/Limelight/Database/Host.m b/Limelight/Database/Host.m index 74135181..fbe63b22 100644 --- a/Limelight/Database/Host.m +++ b/Limelight/Database/Host.m @@ -11,7 +11,13 @@ @implementation Host -@dynamic address; @dynamic name; +@dynamic address; +@dynamic localAddress; +@dynamic externalAddress; +@dynamic uuid; +@dynamic mac; +@dynamic pairState; +@synthesize online; @end diff --git a/Limelight/Limelight.xcdatamodeld/Limelight 0.3.0.xcdatamodel/contents b/Limelight/Limelight.xcdatamodeld/Limelight 0.3.0.xcdatamodel/contents index b710da93..bb906f4d 100644 --- a/Limelight/Limelight.xcdatamodeld/Limelight 0.3.0.xcdatamodel/contents +++ b/Limelight/Limelight.xcdatamodeld/Limelight 0.3.0.xcdatamodel/contents @@ -2,7 +2,12 @@ + + + + + @@ -12,7 +17,7 @@ - + \ No newline at end of file diff --git a/Limelight/Network/DiscoveryManager.h b/Limelight/Network/DiscoveryManager.h new file mode 100644 index 00000000..5f885020 --- /dev/null +++ b/Limelight/Network/DiscoveryManager.h @@ -0,0 +1,27 @@ +// +// DiscoveryManager.h +// Limelight +// +// Created by Diego Waxemberg on 1/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "MDNSManager.h" +#import "Host.h" + +@protocol DiscoveryCallback + +- (void) updateAllHosts:(NSArray*)hosts; + +@end + +@interface DiscoveryManager : NSOperation + +- (id) initWithHosts:(NSArray*)hosts andCallback:(id) callback; +- (void) startDiscovery; +- (void) stopDiscovery; +- (void) addHostToDiscovery:(Host*)host; +- (void) discoverHost:(NSString*)hostAddress withCallback:(void (^)(Host*))callback; + +@end diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m new file mode 100644 index 00000000..8f6f3ba5 --- /dev/null +++ b/Limelight/Network/DiscoveryManager.m @@ -0,0 +1,149 @@ +// +// DiscoveryManager.m +// Limelight +// +// Created by Diego Waxemberg on 1/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "DiscoveryManager.h" +#import "CryptoManager.h" +#import "HttpManager.h" +#import "Utils.h" +#import "DataManager.h" + +@implementation DiscoveryManager { + NSMutableArray* _hostQueue; + NSMutableArray* _discoveredHosts; + id _callback; + MDNSManager* _mdnsMan; + NSOperationQueue* _opQueue; + NSString* _uniqueId; + NSData* _cert; + BOOL shouldDiscover; +} + +- (id)initWithHosts:(NSArray *)hosts andCallback:(id)callback { + self = [super init]; + _hostQueue = [NSMutableArray arrayWithArray:hosts]; + _callback = callback; + _discoveredHosts = [[NSMutableArray alloc] init]; + _opQueue = [[NSOperationQueue alloc] init]; + _mdnsMan = [[MDNSManager alloc] initWithCallback:self]; + [CryptoManager generateKeyPairUsingSSl]; + _uniqueId = [CryptoManager getUniqueID]; + _cert = [CryptoManager readCertFromFile]; + shouldDiscover = NO; + return self; +} + +- (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(Host *))callback { + HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + + Host* host = nil; + if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { + host = [[[DataManager alloc] init] createHost]; + host.address = hostAddress; + [self updateHost:host withServerInfo:serverInfoData]; + [self addHostToDiscovery:host]; + } + callback(host); +} + +- (void) discoverHost:(Host*)host { + if (!shouldDiscover) return; + BOOL receivedResponse = NO; + if (host.localAddress != nil) { + HttpManager* hMan = [[HttpManager alloc] initWithHost:host.localAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { + [self updateHost:host withServerInfo:serverInfoData]; + host.address = host.localAddress; + receivedResponse = YES; + } + } + if (shouldDiscover && !receivedResponse && host.externalAddress != nil) { + HttpManager* hMan = [[HttpManager alloc] initWithHost:host.externalAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { + [self updateHost:host withServerInfo:serverInfoData]; + host.address = host.externalAddress; + receivedResponse = YES; + } + } + + if (shouldDiscover && !receivedResponse && host.address != nil) { + HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { + [self updateHost:host withServerInfo:serverInfoData]; + receivedResponse = YES; + } + } + host.online = receivedResponse; + if (receivedResponse) { + NSLog(@"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n}", host.name, host.address, host.localAddress, host.externalAddress, host.uuid, host.mac, host.pairState, host.online); + } + // If the host has already been discovered, we update the reference + BOOL hostInList = NO; + for (int i = 0; i < _discoveredHosts.count; i++) { + Host* discoveredHost = [_discoveredHosts objectAtIndex:i]; + if ([discoveredHost.uuid isEqualToString:host.uuid]) { + [_discoveredHosts removeObject:discoveredHost]; + [_discoveredHosts insertObject:host atIndex:i]; + hostInList = YES; + } + } + if (!hostInList) { + [_discoveredHosts addObject:host]; + } +} + +- (void) updateHost:(Host*)host withServerInfo:(NSData*)serverInfoData { + host.name = [HttpManager getStringFromXML:serverInfoData tag:@"hostname"]; + host.externalAddress = [HttpManager getStringFromXML:serverInfoData tag:@"ExternalIP"]; + host.localAddress = [HttpManager getStringFromXML:serverInfoData tag:@"LocalIP"]; + host.uuid = [HttpManager getStringFromXML:serverInfoData tag:@"uniqueid"]; + host.mac = [HttpManager getStringFromXML:serverInfoData tag:@"mac"]; + NSString* pairState = [HttpManager getStringFromXML:serverInfoData tag:@"PairStatus"]; + if ([pairState isEqualToString:@"1"]) { + host.pairState = PairStatePaired; + } else { + host.pairState = PairStateUnpaired; + } +} + +- (void) startDiscovery { + NSLog(@"Starting discovery"); + shouldDiscover = YES; + [_mdnsMan searchForHosts]; + [_opQueue addOperation:self]; +} + +- (void) stopDiscovery { + shouldDiscover = NO; + [_mdnsMan stopSearching]; +} + +- (void) addHostToDiscovery:(Host *)host { + [_hostQueue addObject:host]; +} + +- (void)updateHosts:(NSArray *)hosts { + [_hostQueue addObjectsFromArray:hosts]; +} + +- (void)main { + while (shouldDiscover && !self.isCancelled) { + NSLog(@"Running discovery loop"); + [_discoveredHosts removeAllObjects]; + for (Host* host in _hostQueue) { + [self discoverHost:host]; + } + [_callback updateAllHosts:_discoveredHosts]; + [NSThread sleepForTimeInterval:3.0f]; + } +} + +@end diff --git a/Limelight/Network/MDNSManager.m b/Limelight/Network/MDNSManager.m index ecc1b2a2..b966cd3c 100644 --- a/Limelight/Network/MDNSManager.m +++ b/Limelight/Network/MDNSManager.m @@ -7,7 +7,8 @@ // #import "MDNSManager.h" -#import "Computer.h" +#import "Host.h" +#import "DataManager.h" @implementation MDNSManager { NSNetServiceBrowser* mDNSBrowser; @@ -46,9 +47,12 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; - (NSArray*) getFoundHosts { NSMutableArray* hosts = [[NSMutableArray alloc] init]; + DataManager* dataMan = [[DataManager alloc] init]; for (NSNetService* service in services) { if (service.hostName != nil) { - [hosts addObject:[[Computer alloc] initWithHost:service]]; + Host* host = [dataMan createHost]; + host.address = service.hostName; + [hosts addObject:host]; } } return hosts; diff --git a/Limelight/UIComputerView.h b/Limelight/UIComputerView.h index 74b282a5..cd96624c 100644 --- a/Limelight/UIComputerView.h +++ b/Limelight/UIComputerView.h @@ -7,18 +7,18 @@ // #import -#import "Computer.h" +#import "Host.h" @protocol HostCallback -- (void) hostClicked:(Computer*)computer; +- (void) hostClicked:(Host*)host; - (void) addHostClicked; @end @interface UIComputerView : UIView -- (id) initWithComputer:(Computer*)computer andCallback:(id)callback; +- (id) initWithComputer:(Host*)host andCallback:(id)callback; - (id) initForAddWithCallback:(id)callback; - +- (NSString*) online; @end diff --git a/Limelight/UIComputerView.m b/Limelight/UIComputerView.m index 7cc72450..02938cd6 100644 --- a/Limelight/UIComputerView.m +++ b/Limelight/UIComputerView.m @@ -9,9 +9,11 @@ #import "UIComputerView.h" @implementation UIComputerView { - Computer* _computer; + Host* _host; UIButton* _hostButton; UILabel* _hostLabel; + UILabel* _hostStatus; + UILabel* _hostPairState; id _callback; CGSize _labelSize; } @@ -29,27 +31,61 @@ static int LABEL_DY = 20; _hostButton.layer.shadowOpacity = 0.7; _hostLabel = [[UILabel alloc] init]; - + _hostStatus = [[UILabel alloc] init]; + _hostPairState = [[UILabel alloc] init]; return self; } -- (id) initWithComputer:(Computer*)computer andCallback:(id)callback { +- (id) initWithComputer:(Host*)host andCallback:(id)callback { self = [self init]; - _computer = computer; + _host = host; _callback = callback; - [_hostLabel setText:[_computer displayName]]; + [_hostLabel setText:_host.name]; [_hostLabel sizeToFit]; _hostLabel.textColor = [UIColor whiteColor]; + + + switch (host.pairState) { + case PairStateUnknown: + [_hostPairState setText:@"Pair State Unknown"]; + break; + case PairStateUnpaired: + [_hostPairState setText:@"Not Paired"]; + break; + case PairStatePaired: + [_hostPairState setText:@"Paired"]; + break; + } + [_hostPairState sizeToFit]; + _hostPairState.textColor = [UIColor whiteColor]; + + if (host.online) { + _hostStatus.text = @"Online"; + _hostStatus.textColor = [UIColor greenColor]; + } else { + _hostStatus.text = @"Offline"; + _hostStatus.textColor = [UIColor grayColor]; + } + [_hostStatus sizeToFit]; + [_hostButton addTarget:self action:@selector(hostClicked) forControlEvents:UIControlEventTouchUpInside]; _hostLabel.center = CGPointMake(_hostButton.frame.origin.x + (_hostButton.frame.size.width / 2), _hostButton.frame.origin.y + _hostButton.frame.size.height + LABEL_DY); + _hostPairState.center = CGPointMake(_hostLabel.center.x, _hostLabel.center.y + LABEL_DY); + _hostStatus.center = CGPointMake(_hostPairState.center.x, _hostPairState.center.y + LABEL_DY); + [self updateBounds]; [self addSubview:_hostButton]; [self addSubview:_hostLabel]; - + [self addSubview:_hostStatus]; + [self addSubview:_hostPairState]; return self; } +- (NSString *)online { + return _hostStatus.text; +} + - (void) updateBounds { float x = _hostButton.frame.origin.x < _hostLabel.frame.origin.x ? _hostButton.frame.origin.x : _hostLabel.frame.origin.x; float y = _hostButton.frame.origin.y < _hostLabel.frame.origin.y ? _hostButton.frame.origin.y : _hostLabel.frame.origin.y; @@ -83,19 +119,11 @@ static int LABEL_DY = 20; } - (void) hostClicked { - [_callback hostClicked:_computer]; + [_callback hostClicked:_host]; } - (void) addClicked { [_callback addHostClicked]; } -/* -// Only override drawRect: if you perform custom drawing. -// An empty implementation adversely affects performance during animation. -- (void)drawRect:(CGRect)rect { - // Drawing code -} -*/ - @end diff --git a/Limelight/Utility/Computer.h b/Limelight/Utility/Computer.h deleted file mode 100644 index 854a3dd4..00000000 --- a/Limelight/Utility/Computer.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Computer.h -// Limelight -// -// Created by Diego Waxemberg on 10/14/14. -// Copyright (c) 2014 Limelight Stream. All rights reserved. -// - -#import - -@interface Computer : NSObject -@property NSString* displayName; -@property NSString* hostName; -@property BOOL paired; - -- (id) initWithHost:(NSNetService*)host; -- (id) initWithIp:(NSString*)host; - -@end diff --git a/Limelight/Utility/Computer.m b/Limelight/Utility/Computer.m deleted file mode 100644 index fd8f5433..00000000 --- a/Limelight/Utility/Computer.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// Computer.m -// Limelight -// -// Created by Diego Waxemberg on 10/14/14. -// Copyright (c) 2014 Limelight Stream. All rights reserved. -// - -#import "Computer.h" - -@implementation Computer - -- (id) initWithHost:(NSNetService *)host { - self = [super init]; - - self.hostName = [host hostName]; - self.displayName = [host name]; - - return self; -} - -- (id) initWithIp:(NSString*)host { - self = [super init]; - - self.hostName = host; - self.displayName = host; - - return self; -} - -- (BOOL)isEqual:(id)object { - if ([object isKindOfClass:[Computer class]]) { - return [self.hostName isEqual:[object valueForKey:@"hostName"]]; - } else { - return NO; - } -} - -- (NSUInteger)hash { - NSUInteger prime = 31; - NSUInteger result = 1; - result = prime * result + (self.hostName == nil ? 0 : [self.hostName hash]); - - return result; -} - -@end diff --git a/Limelight/Utility/Utils.h b/Limelight/Utility/Utils.h index b9be2d0a..41e1e972 100644 --- a/Limelight/Utility/Utils.h +++ b/Limelight/Utility/Utils.h @@ -10,6 +10,14 @@ @interface Utils : NSObject +typedef NS_ENUM(int, PairState) { + PairStateUnknown, + PairStateUnpaired, + PairStatePaired +}; + +FOUNDATION_EXPORT NSString *const deviceName; + + (NSData*) randomBytes:(NSInteger)length; + (NSString*) bytesToHex:(NSData*)data; + (NSData*) hexToBytes:(NSString*) hex; diff --git a/Limelight/Utility/Utils.m b/Limelight/Utility/Utils.m index 96d678a6..d44c32cc 100644 --- a/Limelight/Utility/Utils.m +++ b/Limelight/Utility/Utils.m @@ -13,6 +13,7 @@ #include @implementation Utils +NSString *const deviceName = @"roth"; + (NSData*) randomBytes:(NSInteger)length { char* bytes = malloc(length); diff --git a/Limelight/ViewControllers/MainFrameViewController.h b/Limelight/ViewControllers/MainFrameViewController.h index 60ccbcab..48ab399a 100644 --- a/Limelight/ViewControllers/MainFrameViewController.h +++ b/Limelight/ViewControllers/MainFrameViewController.h @@ -7,7 +7,7 @@ // #import -#import "MDNSManager.h" +#import "DiscoveryManager.h" #import "PairManager.h" #import "StreamConfiguration.h" #import "UIComputerView.h" @@ -15,7 +15,7 @@ #import "AppManager.h" #import "SWRevealViewController.h" -@interface MainFrameViewController : UICollectionViewController +@interface MainFrameViewController : UICollectionViewController @property (strong, nonatomic) IBOutlet UIButton *limelightLogoButton; @property (weak, nonatomic) IBOutlet UIBarButtonItem *computerNameButton; diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 7fb38693..a6ebae35 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -6,7 +6,6 @@ // #import "MainFrameViewController.h" -#import "Computer.h" #import "CryptoManager.h" #import "HttpManager.h" #import "Connection.h" @@ -22,17 +21,15 @@ @implementation MainFrameViewController { NSOperationQueue* _opQueue; - MDNSManager* _mDNSManager; - Computer* _selectedHost; + Host* _selectedHost; NSString* _uniqueId; NSData* _cert; NSString* _currentGame; - + DiscoveryManager* _discMan; UIAlertView* _pairAlert; UIScrollView* hostScrollView; int currentPosition; } -static NSString* deviceName = @"roth"; static NSMutableSet* hostList; static NSArray* appList; static StreamConfiguration* streamConfig; @@ -66,13 +63,14 @@ static StreamConfiguration* streamConfig; - (void)alreadyPaired { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.hostName uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* appListResp = [hMan executeRequestSynchronously:[hMan newAppListRequest]]; - appList = [HttpManager getAppListFromXML:appListResp]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateApps]; - _computerNameButton.title = _selectedHost.displayName; + NSLog(@"Setting _computerNameButton.title: %@", _selectedHost.name); + _computerNameButton.title = _selectedHost.name; }); + HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + NSData* appListResp = [hMan executeRequestSynchronously:[hMan newAppListRequest]]; + appList = [HttpManager getAppListFromXML:appListResp]; [AppManager retrieveAppAssets:appList withManager:hMan andCallback:self]; }); @@ -97,11 +95,11 @@ static StreamConfiguration* streamConfig; [self presentViewController:alert animated:YES completion:nil]; } -- (void) hostClicked:(Computer *)computer { - NSLog(@"Clicked host: %@", computer.displayName); - _selectedHost = computer; +- (void) hostClicked:(Host *)host { + NSLog(@"Clicked host: %@", host.name); + _selectedHost = host; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:computer.hostName uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; if ([[HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"] isEqualToString:@"1"]) { NSLog(@"Already Paired"); @@ -119,17 +117,24 @@ static StreamConfiguration* streamConfig; UIAlertController* alertController = [UIAlertController alertControllerWithTitle:@"Host Address" message:@"Please enter a hostname or IP address" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - NSString* host = ((UITextField*)[[alertController textFields] objectAtIndex:0]).text; - Computer* newHost = [[Computer alloc] initWithIp:host]; - [hostList addObject:newHost]; - [self updateHosts:[hostList allObjects]]; - DataManager* dataMan = [[DataManager alloc] init]; - [dataMan createHost:newHost.displayName hostname:newHost.hostName]; - [dataMan saveHosts]; - - //TODO: get pair state - - + NSString* hostAddress = ((UITextField*)[[alertController textFields] objectAtIndex:0]).text; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + [_discMan discoverHost:hostAddress withCallback:^(Host* host){ + if (host != nil) { + DataManager* dataMan = [[DataManager alloc] init]; + [dataMan saveHosts]; + dispatch_async(dispatch_get_main_queue(), ^{ + [hostList addObject:host]; + [self updateHosts]; + }); + } else { + UIAlertController* hostNotFoundAlert = [UIAlertController alertControllerWithTitle:@"Host Not Found" message:[NSString stringWithFormat:@"Unable to connect to host: \n%@", hostAddress] preferredStyle:UIAlertControllerStyleAlert]; + [hostNotFoundAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self presentViewController:hostNotFoundAlert animated:YES completion:nil]; + }); + } + }];}); }]]; [alertController addTextFieldWithConfigurationHandler:nil]; [self presentViewController:alertController animated:YES completion:nil]; @@ -138,8 +143,8 @@ static StreamConfiguration* streamConfig; - (void) appClicked:(App *)app { NSLog(@"Clicked app: %@", app.appName); streamConfig = [[StreamConfiguration alloc] init]; - streamConfig.host = _selectedHost.hostName; - streamConfig.hostAddr = [Utils resolveHost:_selectedHost.hostName]; + streamConfig.host = _selectedHost.address; + streamConfig.hostAddr = [Utils resolveHost:_selectedHost.address]; streamConfig.appID = app.appId; if (streamConfig.hostAddr == 0) { [self displayDnsFailedDialog]; @@ -169,7 +174,7 @@ static StreamConfiguration* streamConfig; [alertController addAction:[UIAlertAction actionWithTitle:@"Quit App" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ NSLog(@"Quitting application: %@", currentApp.appName); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.hostName uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; [hMan executeRequestSynchronously:[hMan newQuitAppRequest]]; // TODO: handle failure to quit app currentApp.isRunning = NO; @@ -198,15 +203,6 @@ static StreamConfiguration* streamConfig; return nil; } -- (App*) findAppInAppList:(NSString*)appId { - for (App* app in appList) { - if ([app.appId isEqualToString:appId]) { - return app; - } - } - return nil; -} - - (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position { // If we moved back to the center position, we should save the settings if (position == FrontViewPositionLeft) { @@ -253,7 +249,9 @@ static StreamConfiguration* streamConfig; [hostScrollView setShowsHorizontalScrollIndicator:NO]; [self retrieveSavedHosts]; - [self updateHosts:[hostList allObjects]]; + _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; + + [self updateHosts]; [self.view addSubview:hostScrollView]; } @@ -267,8 +265,7 @@ static StreamConfiguration* streamConfig; [self.navigationController.navigationBar setShadowImage:fakeImage]; [self.navigationController.navigationBar setBackgroundImage:fakeImage forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault]; - _mDNSManager = [[MDNSManager alloc] initWithCallback:self]; - [_mDNSManager searchForHosts]; + [_discMan startDiscovery]; // This will refresh the applist if (_selectedHost != nil) { @@ -279,28 +276,38 @@ static StreamConfiguration* streamConfig; - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - [_mDNSManager stopSearching]; + // when discovery stops, we must create a new instance because you cannot restart an NSOperation when it is finished + [_discMan stopDiscovery]; + _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; + // In case the host objects were updated in the background + [[[DataManager alloc] init] saveHosts]; } - (void) retrieveSavedHosts { - //TODO: Get rid of Computer and only use Host - DataManager* dataMan = [[DataManager alloc] init]; NSArray* hosts = [dataMan retrieveHosts]; - for (Host* host in hosts) { - Computer* comp = [[Computer alloc] initWithIp:host.address]; - comp.displayName = host.name; - [hostList addObject:comp]; - } + [hostList addObjectsFromArray:hosts]; } -- (void)updateHosts:(NSArray *)hosts { - [hostList addObjectsFromArray:hosts]; +- (void) updateAllHosts:(NSArray *)hosts { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"New host list:"); + for (Host* host in hosts) { + NSLog(@"Host: \n{\n\t name:%@ \n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n}", host.name, host.address, host.localAddress, host.externalAddress, host.uuid, host.mac, host.pairState, host.online); + } + [hostList removeAllObjects]; + [hostList addObjectsFromArray:hosts]; + [self updateHosts]; + }); +} + +- (void)updateHosts { + NSLog(@"Updating hosts..."); [[hostScrollView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; UIComputerView* addComp = [[UIComputerView alloc] initForAddWithCallback:self]; UIComputerView* compView; float prevEdge = -1; - for (Computer* comp in hostList) { + for (Host* comp in hostList) { compView = [[UIComputerView alloc] initWithComputer:comp andCallback:self]; compView.center = CGPointMake([self getCompViewX:compView addComp:addComp prevEdge:prevEdge], hostScrollView.frame.size.height / 2); prevEdge = compView.frame.origin.x + compView.frame.size.width;