finished adding error checking for launcher

This commit is contained in:
dante ruiz 2019-06-28 13:35:24 -07:00
parent d20f50fa3c
commit 6a5c9097c9
11 changed files with 135 additions and 158 deletions

View file

@ -22,7 +22,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="MYh-TA-w2A"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iAf-Ee-jJL">
<rect key="frame" x="95" y="344" width="325" height="33"/>
<rect key="frame" x="95" y="336" width="325" height="41"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Choose a display name" id="GzZ-0d-2JH">
<font key="font" metaFont="systemBold" size="28"/>
@ -70,7 +70,7 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0CN-kS-xZ5">
<rect key="frame" x="66" y="68" width="266" height="17"/>
<rect key="frame" x="66" y="68" width="271" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="By signing in, you agree to the High Fidelity" id="zSf-YA-osu">
<font key="font" metaFont="system"/>

View file

@ -36,7 +36,7 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FkC-xi-3UI">
<rect key="frame" x="156" y="105" width="192" height="17"/>
<rect key="frame" x="156" y="103" width="202" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="We seem to have a problem." id="bX3-v1-LLM">
<font key="font" metaFont="system" size="15"/>
@ -45,7 +45,7 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tV0-Su-eLX">
<rect key="frame" x="156" y="80" width="192" height="17"/>
<rect key="frame" x="156" y="78" width="192" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please restart Launcher" id="aBw-o2-xZE">
<font key="font" metaFont="system" size="15"/>

View file

@ -94,7 +94,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hIC-qf-Abj">
<rect key="frame" x="63" y="337" width="384" height="33"/>
<rect key="frame" x="63" y="330" width="384" height="40"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please log in" id="YuE-2K-HLT">
<font key="font" metaFont="systemBold" size="28"/>

View file

@ -1,10 +1,7 @@
#import <Cocoa/Cocoa.h>
@interface CredentialsRequest : NSObject {
@interface CredentialsRequest : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate> {
}
@property (nonatomic, retain) NSMutableData* webData;
@property (nonatomic, retain) NSString* jsonString;
- (void) confirmCredentials:(NSString*)username :(NSString*)password;
@end

View file

@ -2,6 +2,11 @@
#import "Launcher.h"
#import "Settings.h"
@interface CredentialsRequest ()
@property (nonatomic, assign) NSMutableData* receivedData;
@property (nonatomic, assign) NSInteger statusCode;
@end
@implementation CredentialsRequest
- (void) confirmCredentials:(NSString*)username :(NSString*)password {
@ -21,74 +26,69 @@
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Credentials: request finished");
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
NSInteger statusCode = [ne statusCode];
NSLog(@"Credentials: Response status code: %ld", statusCode);
NSLog(@"dante");
if (!error) {
if (statusCode == 200) {
NSLog(@"--->");
NSMutableData* webData = [NSMutableData data];
[webData appendData:data];
NSLog(@"---kdjf");
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError* jsonError;
NSLog(@"fiest");
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
NSLog(@"last");
if (jsonError) {
NSLog(@"Credentials: Failed to parse json -> error: %@", jsonError);
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
return;
}
if (json[@"error"] != nil) {
NSLog(@"Credentials: Login failed -> error: %@", json[@"error"]);
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
});
} else {
NSLog(@"Credentials: Login Successful");
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[[Settings sharedSettings] login:TRUE];
[sharedLauncher setTokenString:jsonString];
[sharedLauncher credentialsAccepted:TRUE];
});
}
} else if (statusCode == 403 || statusCode == 404 || statusCode == 401) {
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
}
} else {
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
}
}];
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request];
[dataTask resume];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
self.receivedData = nil;
self.receivedData = [[NSMutableData alloc] init];
[self.receivedData setLength:0];
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
self.statusCode = [ne statusCode];
NSLog(@"Credentials Response status code: %ld", self.statusCode);
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
NSLog(@"Credentials: did recieve data");
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (error) {
NSLog(@"Credentials: Request completed with an error -> error: %@", error);
[sharedLauncher displayErrorPage];
} else {
if (self.statusCode == 200) {
NSString* jsonString = [[NSString alloc] initWithBytes: [self.receivedData mutableBytes] length:[self.receivedData length] encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError* jsonError = nil;
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
if (jsonError) {
NSLog(@"Credentials: Failed to parse json -> error: %@", jsonError);
[sharedLauncher displayErrorPage];
return;
}
if (json[@"error"] != nil) {
NSLog(@"Credentials: Login failed -> error: %@", json[@"error"]);
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
} else {
NSLog(@"Credentials: Login succeeded");
[[Settings sharedSettings] login:TRUE];
[sharedLauncher setTokenString:jsonString];
[sharedLauncher credentialsAccepted:TRUE];
}
} else if (self.statusCode == 403 || self.statusCode == 404 || self.statusCode == 401) {
NSLog(@"Credentials: Log failed with statusCode: %ld", self.statusCode);
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
} else {
[sharedLauncher displayErrorPage];
}
}
}
@end

View file

@ -8,7 +8,7 @@
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:domainContentUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
@ -18,7 +18,7 @@
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"domain content downloaded %d%%", (int)(100.0*prog));
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
@ -27,7 +27,7 @@
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"Did finish downloading to url");
NSError *error;
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:destinationFileName];
@ -36,18 +36,28 @@
{
[fileManager removeItemAtURL:destinationURL error:nil];
}
NSLog(@"%@", location.path);
NSLog(@"%@", destinationURL);
NSLog(@"location: %@", location.path);
NSLog(@"destination: %@", destinationURL);
BOOL success = [fileManager moveItemAtURL:location toURL:destinationURL error:&error];
NSLog(success ? @"TRUE" : @"FALSE");
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (error) {
NSLog(@"DownlodDomainContent: failed to move file to destintation -> error: %@", error);
[sharedLauncher displayErrorPage];
return;
}
[sharedLauncher setDownloadContextFilename:destinationFileName];
NSLog(@"extracting domain content file");
[sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"content"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadContentFilename]]];
BOOL extractionSuccessful = [sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"content"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadContentFilename]]];
if (!extractionSuccessful) {
[sharedLauncher displayErrorPage];
return;
}
NSLog(@"finished extracting content file");
[sharedLauncher domainContentDownloadFinished];
}

View file

@ -29,7 +29,7 @@
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"Did finish downloading to url");
NSError *error;
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getAppPath] stringByAppendingPathComponent:destinationFileName];
@ -41,12 +41,22 @@
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (error) {
NSLog(@"Download Interface: failed to move file to destination -> error: %@", error);
[sharedLauncher displayErrorPage];
return;
}
[sharedLauncher setDownloadFilename:destinationFileName];
NSString* appPath = [sharedLauncher getAppPath];
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
NSLog(@"extract interface zip");
[sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
BOOL success = [sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
if (!success) {
[sharedLauncher displayErrorPage];
return;
}
NSLog(@"finished extracting interface zip");
NSLog(@"starting xattr");
@ -65,8 +75,8 @@
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
if (error) {
NSLog(@"DownloadInterface: did complete with error -> error: %@", error);
[[Launcher sharedLauncher] displayErrorPage];
}
}

View file

@ -66,7 +66,7 @@ typedef enum LoginErrorTypes
- (void) interfaceFinishedDownloading;
- (NSString*) getDownloadPathForContentAndScripts;
- (void) launchInterface;
- (void) extractZipFileAtDestination:(NSString*) destination :(NSString*) file;
- (BOOL) extractZipFileAtDestination:(NSString*) destination :(NSString*) file;
- (BOOL) isWaitingForInterfaceToTerminate;
- (void) setDownloadFilename:(NSString*) aFilename;
- (void) setDownloadContextFilename:(NSString*) aFilename;

View file

@ -51,11 +51,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
selector:@selector(didTerminateApp:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
SplashScreen* splashScreen = [[SplashScreen alloc] initWithNibName:@"SplashScreen" bundle:nil];
[self.window setContentViewController: splashScreen];
[self closeInterfaceIfRunning];
if (!self.waitingForInterfaceToTerminate) {
[self checkLoginStatus];
}
@ -65,12 +65,12 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
{
NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingString:@"/Launcher/"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:TRUE attributes:nil error:&error];
}
return filePath;
}
@ -79,19 +79,26 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
}
- (void) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
- (BOOL) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
{
NSTask* task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/unzip";
task.arguments = @[@"-o", @"-d", destination, file];
[task launch];
[task waitUntilExit];
if (DELETE_ZIP_FILES) {
NSFileManager* fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:file error:NULL];
}
if ([task terminationStatus] != 0) {
NSLog(@"Extracting file failed -> termination status: %d", [task terminationStatus]);
return FALSE;
}
return TRUE;
}
- (void) displayErrorPage
@ -175,7 +182,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
self.domainURL = aDomainURL;
self.domainContentUrl = aDomainContentUrl;
self.domainScriptsUrl = aDomainScriptsUrl;
[[Settings sharedSettings] setDomainUrl:aDomainURL];
}
@ -326,14 +333,14 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (void) launchInterface
{
NSString* launcherPath = [[self getLauncherPath] stringByAppendingString:@"HQ Launcher"];
[[Settings sharedSettings] setLauncherPath:launcherPath];
[[Settings sharedSettings] save];
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]];
NSError *error = nil;
NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"];
NSString* displayName = [ self displayName];
NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUI/"];
@ -361,7 +368,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
@"--no-launcher", nil];
}
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
[NSApp terminate:self];
}

View file

@ -36,59 +36,12 @@ static NSString* const organizationURL = @"https://orgs.highfidelity.com/organiz
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request];
/* NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
NSInteger statusCode = [ne statusCode];
NSLog(@"Organization Response status code: %ld", [ne statusCode]);
if (!error) {
if (statusCode == 200) {
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
if (jsonError) {
NSLog(@"Failed to parse Organzation json -> error: %@", jsonError);
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
return;
}
NSLog(@"getting org done");
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :[json valueForKey:@"scripts_url"]];
[sharedLauncher setLoginErrorState: NONE];
[sharedLauncher organizationRequestFinished:TRUE];
});
} else if (statusCode == 403 || statusCode == 404) {
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setLoginErrorState: ORGANIZATION];
[sharedLauncher organizationRequestFinished:FALSE];
});
} else {
NSLog(@ "Organization Response error: -> %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
}
} else {
dispatch_async(dispatch_get_main_queue(), ^{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayErrorPage];
});
}
}];*/
[dataTask resume];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(@"----------------> ");
self.receivedData = nil;
self.receivedData = [[NSMutableData alloc] init];
[self.receivedData setLength:0];
@ -103,33 +56,33 @@ static NSString* const organizationURL = @"https://orgs.highfidelity.com/organiz
didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
NSLog(@"did recieve data");
NSLog(@"Organization: did recieve data");
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"did complete with error");
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (error) {
// Handle error
}
else {
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSLog(@"Organization: Request completed with an error -> error: %@", error);
[sharedLauncher displayErrorPage];
} else {
if (self.statusCode == 200) {
NSError *jsonError;
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:self.receivedData options:kNilOptions error:&jsonError];
if (jsonError) {
NSLog(@"Failed to parse Organzation json -> error: %@", jsonError);
[sharedLauncher displayErrorPage];
return;
}
NSLog(@"getting org done");
NSLog(@"Organization: getting org file successful");
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :[json valueForKey:@"scripts_url"]];
[sharedLauncher setLoginErrorState: NONE];
[sharedLauncher organizationRequestFinished:TRUE];
} else if (self.statusCode == 403 || self.statusCode == 404) {
NSLog(@"Organization: failed to get org file");
[sharedLauncher setLoginErrorState: ORGANIZATION];
[sharedLauncher organizationRequestFinished:FALSE];
} else {
NSLog(@ "Organization Response error: -> %@", error);
NSLog(@ "Organization: Response error -> statusCode: %ld", self.statusCode);
[sharedLauncher displayErrorPage];
}
}

View file

@ -17,7 +17,7 @@ void redirectLogToDocuments()
int main(int argc, const char* argv[]) {
//NSApp.appearance = [NSAppearance appearanceNamed: NSAppearanceNameAqua];
//redirectLogToDocuments();
redirectLogToDocuments();
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.highfidelity.launcher"];
if ([apps count] > 1) {
NSLog(@"launcher is already running");