Detect user activity in Cocoa app (taps, clicks, ...) - objective-c

For a Mac application, I want to detect user activity in the app, so I can periodically let a web service know that the user is still active on the endpoint.
In Cocoa Touch, I would override sendEvent of UIApplication, but the sendEvent in NSApplication equivalent in Cocoa, doesn't do the same.
Which APIs should I use instead for a Mac application, to detect user activity? Can I perhaps somehow have a global responder hookup from where I can send the pings to my service?
Preferably, I want to listen for actions the user can be expected to perform every 15-30 second, ie. clicks, tabs, typing, switching windows or applications.

You most likely want to create a global event monitor using +[NSEvent addGlobalMonitorForEventsMatchingMask:handler:]. This calls your handler whenever an event whose type matches the passed mask (you should use NSAnyEventMask) is sent to another application. You can observe, but not change, the event here, which suits your usage perfectly. There is one thing to watch out for: the documentation says that you won't receive key events unless your app is trusted for Accessibility.
You can do similarly for events that are routed to your own application with +[NSEvent addLocalMonitorForEventsMatchingMask:handler:].

It's not a notification, but you can query the time since user activity using CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType).

This worked for me:
-(void) addMyApplicationEventsMonitor {
self.localEventsMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskAny handler:^NSEvent * (NSEvent * event) {
// do your stuff here
return event;
}];
}
-(void)dealloc{
//remember add this to remove your monitor
[NSEvent removeMonitor:self.localEventsMonitor];
}
reference here

Related

How to detect and implement touch events for single tap on home button?

Not sure if it is possible but is there any way to detect a single touch on the home button. To start with, I would simply like to add an NSLog if the user touches down once on the home button (without actually pressing), but I don't know where I would add this functionality. Does Apple allow you to interact with the home button?
I looked at the app delegate methods, but I can't see how any would work in a single tap (touch) context. Would really appreciate your help.
Does Apple allow you to interact with the home button?
No, not yet. There are no APIs available to explicitly detect home button interactions.
You can rely on the traditional app delegate lifecycle function invocations to perform any logic you wanted to.
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

Is There A Lost Focus Window Event in Objective C?

Coming from a .NET background I'm used to events getting fired so trapping a lost focus event is easy but I'm not sure how to do this in Obj-C. Basically I want my app to know when another application has gotten focus and it no longer has it so it can perform some actions.
Can you please tell me how I can implement this kind of functionality in Obj-C for an OSX app?
Have a look at the NSWindow notifications. Specifically, you're interested in NSWindowDidBecomeKeyNotification and NSWindowDidResignKeyNotification. You can also create a delegate for the window and implement its windowDidBecomeKey: and windowDidResignKey: methods, as noted in the NSWindowDelegate protocol documentation.
Or, if you just wanted to know when the application (not a window) has gained focus, you can subscribe to the NSApplicationDidBecomeActiveNotification. Likewise, NSApplicationDidResignActiveNotification will notify you when your app loses focus. These notifications are discussed more here. You can also implement applicationWillBecomeActive: and applicationWillResignActive: in the application delegate.
It's unclear if you want notification of a single window losing focus or notification of your entire app losing focus. My answer here provides notification for the entire application losing focus. (See mipadi's answer if you just want to know when one of your app's window loses focus.)
Observe the appropriate notification:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDeactivated:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Then add the handler method:
-(void) appDeactivated:(NSNotification *)notification
{
NSRunningApplication* app = [notification.userInfo objectForKey:#"NSWorkspaceApplicationKey"];
if (app == [NSRunningApplication currentApplication]) {
// your cleanup code here
}
}

How get pusher events when the iOS app go to the background?

I need to get triggered eventos from pusher, and when the app go the background I don't get them (only the first one).
I have this:
#property(strong, nonatomic) PTPusherPresenceChannel *taxi_channel;
PTPusherPresenceChannel *taxi = [PusherController sharedApp].taxi_channel;
- (void)viewDidLoad
#weakify(self);
[taxi bindToEventNamed:#"client-driver-cancel-service" handleWithBlock:^(PTPusherEvent *event)
{
DDLogDebug(#"%#: %#", event.name, event.data);
#strongify(self);
[self cancelServiceAcepted];
}];
The problem is that I get a single event when get into the background, but after the first I don't get them anymore.
I have implemented the code at https://github.com/pusher/pusher-test-iOS/blob/master/Diagnostics/Code/ClientDisconnectionHandler.h
If the app go the foreground it work fine.
I'm the author of libPusher. I answered your question on Github but I thought I'd post it here as it might be helpful for others.
Unfortunately its not really possible to use Pusher in the background and its not really what it is designed for. Pusher works great for receiving events in realtime while your app is running but to get background notifications, you really need to be looking at using Apple push notifications OR period fetch, depending on whether you'd prefer push or pull.
My suggestion would be:
Use Pusher while your app is in the foreground to receive real-time updates
Use push notifications to send significant events to your app while it is in the background (these should be less frequent) AND/OR
Possibly use background fetch to pull the latest changes/events from your server
Restart listening to events from Pusher when your app resumes in the foreground

NSWorkspaceDidActivateApplicationNotification called before application is ready to use (launched)

I'm using both NSWorkspaceDidActivateApplicationNotification and NSWorkspaceDidLaunchApplicationNotification notifications to know which app the user is interacting with.
The problem is that, if an application is just opened and still launching, I first receive a activate notification, and soon afterwards a launch notification.
Is there any way to know within the activate method that the app is still launching and not yet ready for use? (Still bouncing in the dock)
I see that the ichat sample project by apple does not use the above approach and instead only listens to launch notifications. It then uses kAXApplicationActivatedNotification to add an AXObserver to the app. Is this the preferred way? (And also NSRunningApplications to add an observer to all already loaded apps).
I wanted to keep using just plain simple NSNotifications because I think it may be less memory intensive. (No need to keep an observer around for each and every app loaded).
check the NSRunningApplication object passed in the userinfo of the NSWorkspaceDidActivateApplicationNotification
NSRunningApplication *app = [note.userInfo objectForKey:NSWorkspaceApplicationKey];
if(app.isFinishedLaunching)
NSLog(#"up");

iOS – How to deal with notifications when I have no "control" of the GUI

I'm using Apple's view controllers quite alot in my app (like MFMessageComposeViewController and ABPeoplePickerNavigationController). So when I receive a notification (Local or Remote notification) how would I deal with it the most elegant way since I can not interact (send messages) to Apple's view controllers.
My assumption is that if the user is actively using the app and in i.e MFMessageComposeViewController he does not want to get disturbed/interrupted with what he is doing. But if it was me I would get a bit confused if I was doing something and I would hear boing sound (from the notification) and then nothing more happened.
So would a reasonable way to handle this do let the user finish doing his task in whatever Apple view controller he is inside and then display the notification for the user?
Or dismiss the Apple view controller and handle the notifaction and then put the user back in the Apple view controller?
I'm not quite sure I understood you exactly correct, but to me it sound like you want to do something like this:
If the user is composing a message in your app and there is an incoming notification, lets say a push notification from the facebook app, you want to handle this event somehow in your app by letting your MFMessageComposerViewController do something.
As answer to this request I would like to state the following:
You should be able to send messages to your MFMessageComposerViewController if you extend this class (i.e. create a class called MyMessageComposerController and let it extend the standard controller, and do whatever you need there)
However, you can't really do anything about the push notification, it comes from another app and this functionality is built into iOS, you can not make the push arrive later when the user is finished typing, it will always arrive and the user will always be the one to decide if he/she should keep typing or look at the notification. The only thing you can do is to make sure that your app saves everything that the user has typed so that he/she can continue typing when returning to your app.
Assuming you don't just want to display an AlertView or something similarly modal over the message composer, I would do something like the following:
assign a MFMessageComposeViewControllerDelegate to the composer.
show the composer as usual.
queue/remember the notifications received while the composer is in play (through the normal application:didReceive{Local|Remote}Notification: messages)
when the composer is closed, the delegate receives its messageComposeViewController:didFinishWithResult: message.
Do... whatever... with the stored/queued notifications.
Whether you delay responding to the notification until the user is finished with the composition or interrupt them is a trickier situation. If it's a relatively unimportant notification or notifications that are frequent, I'd tend towards the queue/delay approach. But for "important" notifications, I'd have no problems in interrupting the user. Of course, that just moves the question to what determines importance. And that is very much dependent on the app.