added preference handling (logging/new-version check)
added cmd+q hotkey for prefs app to close/quit changed name of installer binary to match app improved device detection (uses default if can't find apple camera/mic) code cleanup/error handling
This commit is contained in:
parent
9789a687ff
commit
d9ae15b3bc
|
@ -5,7 +5,7 @@
|
|||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
|
|
@ -35,14 +35,21 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
//grab first apple camera
|
||||
//grab first apple camera, or default
|
||||
// ->saves into iVar 'camera'
|
||||
// note: could maybe use defaultDeviceWithDeviceType method() to default device...
|
||||
-(void)findAppleCamera
|
||||
{
|
||||
//get cameras
|
||||
// ->look for one that belongs to apple
|
||||
for(AVCaptureDevice* currentCamera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
|
||||
//cameras
|
||||
NSArray* cameras = nil;
|
||||
|
||||
//grab all cameras
|
||||
cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"cameras: %@", cameras]);
|
||||
|
||||
//look for camera that belongs to apple
|
||||
for(AVCaptureDevice* currentCamera in cameras)
|
||||
{
|
||||
//check if apple
|
||||
if(YES == [currentCamera.manufacturer isEqualToString:@"Apple Inc."])
|
||||
|
@ -55,6 +62,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
//didn't find apple
|
||||
// ->grab default camera
|
||||
if(nil == self.camera)
|
||||
{
|
||||
//get default
|
||||
self.camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"didn't find apple camera, grabbed default: %@", self.camera]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -62,9 +80,17 @@
|
|||
// ->saves into iVar 'mic'
|
||||
-(void)findAppleMic
|
||||
{
|
||||
//get mics
|
||||
// ->loof for one that belongs to app
|
||||
for(AVCaptureDevice* currentMic in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio])
|
||||
//mics
|
||||
NSArray* mics = nil;
|
||||
|
||||
//grab all mics
|
||||
mics = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"mics: %@", mics]);
|
||||
|
||||
//look for mic that belongs to apple
|
||||
for(AVCaptureDevice* currentMic in mics)
|
||||
{
|
||||
//check if apple
|
||||
// ->also check input source
|
||||
|
@ -77,7 +103,17 @@
|
|||
//exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//didn't find apple
|
||||
// ->grab default camera
|
||||
if(nil == self.mic)
|
||||
{
|
||||
//get default
|
||||
self.mic = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"didn't find apple 'imic', grabbed default: %@", self.mic]);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -344,9 +380,15 @@ bail:
|
|||
//wait semaphore
|
||||
dispatch_semaphore_t waitSema = nil;
|
||||
|
||||
//init dictionary
|
||||
//devices
|
||||
NSMutableArray* devices = nil;
|
||||
|
||||
//init dictionary for event
|
||||
event = [NSMutableDictionary dictionary];
|
||||
|
||||
//init array for devices
|
||||
devices = [NSMutableArray array];
|
||||
|
||||
//sync
|
||||
@synchronized (self)
|
||||
{
|
||||
|
@ -354,9 +396,23 @@ bail:
|
|||
//set status
|
||||
[self setVideoDevStatus:deviceID];
|
||||
|
||||
//add camera
|
||||
if(nil != self.camera)
|
||||
{
|
||||
//add
|
||||
[devices addObject:@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}];
|
||||
}
|
||||
|
||||
//add mic
|
||||
if(nil != self.mic)
|
||||
{
|
||||
//add
|
||||
[devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}];
|
||||
}
|
||||
|
||||
//send msg to status menu
|
||||
// ->update menu to show (all) devices & their status
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)},@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}]];
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
|
||||
|
||||
//add device
|
||||
event[EVENT_DEVICE] = self.camera;
|
||||
|
@ -610,6 +666,9 @@ bail:
|
|||
//ret var
|
||||
BOOL bRegistered = NO;
|
||||
|
||||
//status var
|
||||
OSStatus status = -1;
|
||||
|
||||
//property struct
|
||||
AudioObjectPropertyAddress propertyStruct = {0};
|
||||
|
||||
|
@ -629,22 +688,24 @@ bail:
|
|||
[self handleAudioNotification:deviceID];
|
||||
};
|
||||
|
||||
|
||||
OSStatus ret = AudioObjectAddPropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_main_queue(), listenerBlock);
|
||||
if (ret)
|
||||
//add property listener for audio changes
|
||||
status = AudioObjectAddPropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_main_queue(), listenerBlock);
|
||||
if(noErr != status)
|
||||
{
|
||||
abort(); // FIXME
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"AudioObjectAddPropertyListenerBlock() failed with %d", status]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//happy
|
||||
bRegistered = YES;
|
||||
|
||||
//bail
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return bRegistered;
|
||||
|
||||
}
|
||||
|
||||
//build and display notification
|
||||
|
@ -665,6 +726,9 @@ bail:
|
|||
//log msg
|
||||
NSMutableString* logMsg = nil;
|
||||
|
||||
//preferences
|
||||
NSDictionary* preferences = nil;
|
||||
|
||||
//alloc notificaiton
|
||||
notification = [[NSUserNotification alloc] init];
|
||||
|
||||
|
@ -677,6 +741,9 @@ bail:
|
|||
//alloc log msg
|
||||
logMsg = [NSMutableString string];
|
||||
|
||||
//always (manually) load preferences
|
||||
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
||||
|
||||
//set title
|
||||
// ->audio device
|
||||
if(YES == [event[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")])
|
||||
|
@ -707,6 +774,10 @@ bail:
|
|||
[title appendString:@" became active"];
|
||||
}
|
||||
|
||||
//set details
|
||||
// ->name of device
|
||||
details = ((AVCaptureDevice*)event[EVENT_DEVICE]).localizedName;
|
||||
|
||||
//customize buttons
|
||||
// ->for mic or inactive events, just say 'ok'
|
||||
if( (YES == [event[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")]) ||
|
||||
|
@ -742,8 +813,7 @@ bail:
|
|||
}
|
||||
|
||||
//log event?
|
||||
// TODO: test will final apps/prefs
|
||||
if(YES == [[NSUserDefaults standardUserDefaults] boolForKey:LOG_ACTIVITY])
|
||||
if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue])
|
||||
{
|
||||
//init msg
|
||||
[logMsg appendString:@"OVERSIGHT: "];
|
||||
|
@ -768,26 +838,11 @@ bail:
|
|||
syslog(LOG_ERR, "%s\n", logMsg.UTF8String);
|
||||
}
|
||||
|
||||
//icon issues
|
||||
//http://stackoverflow.com/questions/11856766/osx-notification-center-icon
|
||||
|
||||
//contentImage for process icon?
|
||||
|
||||
//custom icon?
|
||||
//http://stackoverflow.com/questions/24923979/nsusernotification-customisable-app-icon
|
||||
|
||||
//icon set automatically?
|
||||
//[notification set]
|
||||
|
||||
//set title
|
||||
[notification setTitle:title];
|
||||
|
||||
//set subtitle
|
||||
[notification setSubtitle:((AVCaptureDevice*)event[EVENT_DEVICE]).localizedName];
|
||||
|
||||
//set details
|
||||
// ->name of process using it / icon too?
|
||||
//[notification setInformativeText:@"some process (1337)"];
|
||||
[notification setSubtitle:details];
|
||||
|
||||
//set notification
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
// ->load status bar and kick off monitor
|
||||
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
//default
|
||||
NSDictionary* preferences = nil;
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"starting login item");
|
||||
|
||||
|
@ -37,9 +40,23 @@
|
|||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"initialized/loaded status bar (icon/menu)");
|
||||
|
||||
//first time, register defaults (manually cuz NSUserDefaults wasn't working - wtf)
|
||||
// ->note: do this in here, since main app (with prefs) isn't run until user manually launches it
|
||||
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:[APP_PREFERENCES stringByExpandingTildeInPath]])
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"preference file not found; manually creating");
|
||||
|
||||
//write em out
|
||||
[@{PREF_LOG_ACTIVITY:@YES, PREF_CHECK_4_UPDATES:@YES} writeToFile:[APP_PREFERENCES stringByExpandingTildeInPath] atomically:NO];
|
||||
}
|
||||
|
||||
//always (manually) load preferences
|
||||
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
||||
|
||||
//check for updates
|
||||
// ->but only when user has not disabled that feature
|
||||
if(YES == [[NSUserDefaults standardUserDefaults] boolForKey:CHECK_4_UPDATES])
|
||||
if(YES == [preferences[PREF_CHECK_4_UPDATES] boolValue])
|
||||
{
|
||||
//after a minute
|
||||
//->check for updates in background
|
||||
|
@ -51,7 +68,6 @@
|
|||
//check
|
||||
[self isThereAndUpdate];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//create/init av event monitor
|
||||
|
|
|
@ -233,38 +233,32 @@
|
|||
// ->launch main application which will show prefs
|
||||
-(void)preferences:(id)sender
|
||||
{
|
||||
//path components
|
||||
NSArray* pathComponents = nil;
|
||||
|
||||
//this works when packaged into Login Item into top-level app
|
||||
NSArray *pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents];
|
||||
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 4)];
|
||||
NSString *path = [NSString pathWithComponents:pathComponents];
|
||||
[[NSWorkspace sharedWorkspace] launchApplication:path];
|
||||
//path to main app
|
||||
NSString* mainApp = nil;
|
||||
|
||||
//init path components
|
||||
pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents];
|
||||
if(pathComponents.count <= 4)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
//controller for preferences window
|
||||
PrefsWindowController* prefsWindowController = nil;
|
||||
//init path to main app
|
||||
// ->basically trim off last 4 path components
|
||||
mainApp = [NSString pathWithComponents:[pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 4)]];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"displaying preferences window");
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"launching main app; %@", mainApp]);
|
||||
|
||||
//grab controller
|
||||
prefsWindowController = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).prefsWindowController;
|
||||
|
||||
//show pref window
|
||||
[prefsWindowController showWindow:sender];
|
||||
|
||||
//invoke function in background that will make window modal
|
||||
// ->waits until window is non-nil
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
//make modal
|
||||
makeModal(prefsWindowController);
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
//launch main app
|
||||
[[NSWorkspace sharedWorkspace] launchApplication:mainApp];
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -46,10 +46,14 @@
|
|||
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
//set 'log activity' button state
|
||||
self.logActivity.state = [[NSUserDefaults standardUserDefaults] boolForKey:LOG_ACTIVITY];
|
||||
self.logActivity.state = [[NSUserDefaults standardUserDefaults] boolForKey:PREF_LOG_ACTIVITY];
|
||||
|
||||
//set 'automatically check for updates' button state
|
||||
self.check4Updates.state = [[NSUserDefaults standardUserDefaults] boolForKey:CHECK_4_UPDATES];
|
||||
self.check4Updates.state = [[NSUserDefaults standardUserDefaults] boolForKey:PREF_CHECK_4_UPDATES];
|
||||
|
||||
//register for hotkey presses
|
||||
// ->for now, just cmd+q to quit app
|
||||
[self registerKeypressHandler];
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -60,6 +64,75 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
//register handler for hot keys
|
||||
-(void)registerKeypressHandler
|
||||
{
|
||||
//event handler
|
||||
NSEvent* (^keypressHandler)(NSEvent *) = nil;
|
||||
|
||||
//init handler block
|
||||
// ->just call helper function
|
||||
keypressHandler = ^NSEvent * (NSEvent * theEvent){
|
||||
|
||||
//invoke helper
|
||||
return [self handleKeypress:theEvent];
|
||||
};
|
||||
|
||||
//register for key-down events
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:keypressHandler];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//helper function for keypresses
|
||||
// ->for now, only handle cmd+q, to quit
|
||||
-(NSEvent*)handleKeypress:(NSEvent*)event
|
||||
{
|
||||
//flag indicating event was handled
|
||||
BOOL wasHandled = NO;
|
||||
|
||||
//only care about 'cmd' + something
|
||||
if(NSCommandKeyMask != (event.modifierFlags & NSCommandKeyMask))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//handle key-code
|
||||
// command+q: quite
|
||||
switch ([event keyCode])
|
||||
{
|
||||
//'q' (quit)
|
||||
case KEYCODE_Q:
|
||||
|
||||
//bye!
|
||||
[[NSApplication sharedApplication] terminate:nil];
|
||||
|
||||
//set flag
|
||||
wasHandled = YES;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//nil out event if it was handled
|
||||
if(YES == wasHandled)
|
||||
{
|
||||
//nil
|
||||
event = nil;
|
||||
}
|
||||
|
||||
return event;
|
||||
|
||||
}
|
||||
|
||||
//toggle/set preferences
|
||||
-(IBAction)togglePreference:(NSButton *)sender
|
||||
{
|
||||
|
@ -67,16 +140,19 @@
|
|||
if(sender == self.logActivity)
|
||||
{
|
||||
//set
|
||||
[[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:LOG_ACTIVITY];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:PREF_LOG_ACTIVITY];
|
||||
}
|
||||
|
||||
//set 'automatically check for updates'
|
||||
else if (sender == self.check4Updates)
|
||||
{
|
||||
//set
|
||||
[[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:CHECK_4_UPDATES];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:PREF_CHECK_4_UPDATES];
|
||||
}
|
||||
|
||||
//save em
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -581,6 +581,7 @@
|
|||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
|
@ -617,6 +618,7 @@
|
|||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
// ->for status msg to avoid activity indicator
|
||||
#define FRAME_SHIFT 45
|
||||
|
||||
//hotkey 'w'
|
||||
#define KEYCODE_W 0xD
|
||||
|
||||
//hotkey 'q'
|
||||
#define KEYCODE_Q 0xC
|
||||
|
||||
//OS version x
|
||||
#define OS_MAJOR_VERSION_X 10
|
||||
|
||||
|
@ -79,11 +73,17 @@
|
|||
//general error URL
|
||||
#define FATAL_ERROR_URL @"https://objective-see.com/errors.html"
|
||||
|
||||
//path to preferences
|
||||
#define APP_PREFERENCES @"~/Library/Preferences/com.objective-see.OverSight.plist"
|
||||
|
||||
//log activity button
|
||||
#define LOG_ACTIVITY @"logActivity"
|
||||
#define PREF_LOG_ACTIVITY @"logActivity"
|
||||
|
||||
//automatically check for updates button
|
||||
#define CHECK_4_UPDATES @"check4Updates"
|
||||
#define PREF_CHECK_4_UPDATES @"check4Updates"
|
||||
|
||||
//keycode for 'q'
|
||||
#define KEYCODE_Q 0x0C
|
||||
|
||||
//path to pkill
|
||||
#define PKILL @"/usr/bin/pkill"
|
||||
|
|
Loading…
Reference in New Issue