Objective C: Get notifications about a user's idle state - objective-c

My cocoa app runs background tasks, which I would like to stop when the user becomes idle (no keyboard/mouse input) and then resume when the user becomes active again. Is there a way to register for idle-state notifications?

In case you can't link to Carbon (ie. you want to compile x86_64 bit binary) you can wrap this function (which returns current idle time in seconds resolution as double - CFTimeInterval) in a timer:
#include <IOKit/IOKitLib.h>
CFTimeInterval CFDateGetIdleTimeInterval() {
mach_port_t port;
io_iterator_t iter;
CFTypeRef value = kCFNull;
uint64_t idle = 0;
CFMutableDictionaryRef properties = NULL;
io_registry_entry_t entry;
IOMasterPort(MACH_PORT_NULL, &port);
IOServiceGetMatchingServices(port, IOServiceMatching("IOHIDSystem"), &iter);
if (iter) {
if ((entry = IOIteratorNext(iter))) {
if (IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties) {
if (CFDictionaryGetValueIfPresent(properties, CFSTR("HIDIdleTime"), &value)) {
if (CFGetTypeID(value) == CFDataGetTypeID()) {
CFDataGetBytes(value, CFRangeMake(0, sizeof(idle)), (UInt8 *) &idle);
} else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
CFNumberGetValue(value, kCFNumberSInt64Type, &idle);
}
}
CFRelease(properties);
}
IOObjectRelease(entry);
}
IOObjectRelease(iter);
}
return idle / 1000000000.0;
}
You'll need to link your code to IOKit.framework

There's a Carbon API that will send a notification when there hasn't been a user event after a certain duration called EventLoopIdleTimer. Uli Kusterer has written a Cocoa wrapper for here (look for UKIdleTimer).
If you want something lower level, you may be able to implement the behavior you want with a combination of timers and the CoreGraphics function CGEventSourceSecondsSinceLastEventType (available in <CoreGraphics/CGEventSource.h>).

Apple's Technical Q&A QA1340 Registering and unregistering for sleep and wake notifications may be what you are looking for.
If you need more control than NSWorkspaceWillSleepNotification (Listing 1), use I/O Kit and register to receive power notifications (Listing 3).

I used a different approach.
Subclassing UIApplication I override the sendEvent method filtering touches (actually you can filter any kind of event, acceleration, touches, etc.).
Using a shared variable and a background timer I managed the "idle".
Every time the user touch the screen the variable is set with current timeInterval (current time).
The timer fire method checks for the elapsed time since last touch, if greater than the threshold (in my case was around 90seconds) you can POST your own notification.
I used this simple approach to create a custom set of apps that after some idle time automatically call the "screensaver" app.
Nothing clever, it just do the job.
Hope that helps.

Related

Keep network connection alive in sleep mode for OSX [duplicate]

Is there way to prevent a Mac from going to sleep programmatically using Objective-C? The I/O kit fundamentals section on Apple's dev site tells me that a driver gets notified of an idle / system sleep, but I can't find a way of preventing the system from sleeping. Is it even possible?
I've come across some other solutions using Caffeine, jiggler, sleepless and even AppleScript, but I want to do this in Objective-C. Thanks.
Here is the official Apple documentation (including code snippet):
Technical Q&A QA1340 - How to I prevent sleep?
Quote: Preventing sleep using I/O Kit in Mac OS X 10.6 Snow Leopard:
#import <IOKit/pwr_mgt/IOPMLib.h>
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
// reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess)
{
// Add the work you need to do without
// the system sleeping here.
success = IOPMAssertionRelease(assertionID);
// The system will be able to sleep again.
}
For older OSX version, check the following:
Technical Q&A QA1160 - How can I prevent system sleep while my application is running?
Quote: Example usage of UpdateSystemActivity (the canonical way for < 10.6)
#include <CoreServices/CoreServices.h>
void
MyTimerCallback(CFRunLoopTimerRef timer, void *info)
{
UpdateSystemActivity(OverallAct);
}
int
main (int argc, const char * argv[])
{
CFRunLoopTimerRef timer;
CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL };
timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, MyTimerCallback, &context);
if (timer != NULL) {
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
}
/* Start the run loop to receive timer callbacks. You don't need to
call this if you already have a Carbon or Cocoa EventLoop running. */
CFRunLoopRun();
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
return (0);
}
Apple's Q&A1340 replaces Q&A1160. The latest Q&A answers the question "Q: How can my application get notified when the computer is going to sleep or waking from sleep? How do I prevent sleep?"
Listing 2 of Q&A1340:
#import <IOKit/pwr_mgt/IOPMLib.h>
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
//reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess)
{
//Add the work you need to do without
// the system sleeping here.
success = IOPMAssertionRelease(assertionID);
//The system will be able to sleep again.
}
Note that you can only stop idle time sleep, not sleep triggered by the user.
For applications supporting Mac OS X 10.6 and later, use the new IOPMAssertion family of functions. These functions allow other applications and utilities to see your application's desire not to sleep; this is critical to working seamlessly with third party power management software.
Just create an NSTimer that fires a function with this
UpdateSystemActivity(OverallAct);
I'm pretty sure that that's exactly what Caffeine does.

Detect screen on/off from iOS service

I am developing a network monitor app that runs in background as a service. Is it possible to get a notification/call when the screen is turned on or off?
It exists in Android by using the following code:
private void registerScreenOnOffReceiver()
{
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenOnOffReceiver, filter);
}
screenOnOffReceiver is then called when screen is turned on/off. Is there a similar solution for iOS?
Edit:
The best I've found so far is UIApplicationProtectedDataWillBecomeUnavailable ( Detect if iPhone screen is on/off ) but it require the user to enable Data Protection (password protection) on the device.
You can use Darwin notifications, to listen for the events. I'm not 100% sure, but it looks to me, from running on a jailbroken iOS 5.0.1 iPhone 4, that one of these events might be what you need:
com.apple.iokit.hid.displayStatus
com.apple.springboard.hasBlankedScreen
com.apple.springboard.lockstate
Update: also, the following notification is posted when the phone locks (but not when it unlocks):
com.apple.springboard.lockcomplete
To use this, register for the event like this (this registers for just one event, but if that doesn't work for you, try the others):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.iokit.hid.displayStatus"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
where displayStatusChanged is your event callback:
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSLog(#"event received!");
// you might try inspecting the `userInfo` dictionary, to see
// if it contains any useful info
if (userInfo != nil) {
CFShow(userInfo);
}
}
If you really want this code to run in the background as a service, and you're jailbroken, I would recommend looking into iOS Launch Daemons. As opposed to an app that you simply let run in the background, a launch daemon can start automatically after a reboot, and you don't have to worry about iOS rules for apps running tasks in the background.
Let us know how this works!
Using the lower-level notify API you can query the lockstate when a notification is received:
#import <notify.h>
int notify_token;
notify_register_dispatch("com.apple.springboard.lockstate", &notify_token, dispatch_get_main_queue(), ^(int token) {
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
NSLog(#"com.apple.springboard.lockstate = %llu", state);
});
Of course your app will have to start a UIBackgroundTask in order to get the notifications, which limits the usefulness of this technique due to the limited runtime allowed by iOS.
While iPhone screen is locked appdelegate method
"- (void)applicationWillResignActive:(UIApplication *)application"
will be called you can check that. Hope it may help you.

open al sounds don't play after the incoming call, until app restart

I'm playing game sounds using OpenAL, and bg music using standard AV. Recently i've found that after the
incoming call all openal sounds don't work while bg music is still playing. If I force stop app and start again
sounds appear again. Do smbd happen to know what's happening to openal during/after the incoming call?
Ok, it seems I've found a solution.
I'm using obj-c sound manager, so I just added beginInterruption and endInterruption delegate methods of AVAudioSession (and AVAudioPlayer) to my class.
beginInterruption looks like:
alcMakeContextCurrent(NULL);
and endInterruption looks something like:
NSError * audioSessionError = NULL;
[audioSession setCategory:soundCategory error:&audioSessionError];
if (audioSessionError)
{
Log(#"ERROR - SoundManager: Unable to set the audio session category");
return;
}
// Set the audio session state to true and report any errors
audioSessionError = NULL;
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError)
{
Log(#"ERROR - SoundManager: Unable to set the audio session state to YES with error %d.", (int) result);
return;
}
//music players handling
bool plays = false;
if (musicPlayer[currentPlayer] != nil)
plays = [musicPlayer[currentPlayer] isPlaying];
if (musicPlayer[currentPlayer] != nil && !plays)
[musicPlayer[currentPlayer] play];
alcMakeContextCurrent(context);
Yes, this works if you're using only openAL sounds. But to play long tracks you should use AVAudioPlayer.
But here's the Apple magic again! If you play music along with OpenAL sounds something odd happens.
Cancel the incoming call and AVAudioSessionDelegate::endInterruption with AVAudioPlayerDelegate::audioPlayerEndInterruption will never called. Only beginInterruption, not the end.
Even AppDelegate::applicationWillEnterForeground will not be called, and app just don't know that we've returned.
But the good news is that you can call your endInterruption in AppDelegate::applicationDidBecomeActive method, and openAL context will be restored. And this works!
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (MySoundMngr != nil)
{
[MySoundMngr endInterruption];
}
// Restart any tasks that were paused and so on....
}
I had a hard time figuring this out so wanted to add my answer here. This is all specifically in Xamarin, but I suspect it applies generally and is similar to #Tertium's answer
You can prevent iOS from interrupting your audio in some situations (e.g., getting a phone call but declining it), using AVAudioSession.SharedInstance().SetPrefersNoInterruptionsFromSystemAlerts(true, out NSError err);
You will still be interrupted in some situations (e.g., you accept a phone call). To catch these you must AVAudioSession.Notifications.ObserveInteruption(myAudioInterruptionHandler); when you launch your app.
Inside this handler, you can determine if you're shutting down or coming back like so:
void myAudioInterruptionHandler(object sender, AVAudioSessionInterruptionEventArgs args) {
args.Notification.UserInfo.TryGetValue(
new NSString("AVAudioSessionInterruptionTypeKey"),
out NSObject typeKey
);
bool isBeginningInterruption = (typeKey.ToString() == "1");
// ...
}
Interruption Begins
When the interruption begins, stop whatever audio is playing (you'll need to handle this on your own based on your app, but probably by calling AL.SourceStop on everything).
Then, critically,
ContextHandle audioContextHandle = Alc.GetCurrentContext();
Alc.MakeContextCurrent(ContextHandle.Zero);
If you don't do this right away, iOS will fry your ALC context and you are doomed. Note that if you have a handler for AudioRouteChanged this is too late, you must do it in the AudioInterruption handler.
Interruption Ends
When you're coming back from the interruption, first reboot your iOS audio session:
AVAudioSession.SharedInstance().SetActive(true);
You may also need to reset your preferred input (I think this step is optional if you always use the default input) AVAudioSession.SharedInstance().SetPreferredInput(Input, out NSError err)
Then restore your context
Alc.MakeContextCurrent(audioContextHandle);

How to programmatically prevent a Mac from going to sleep?

Is there way to prevent a Mac from going to sleep programmatically using Objective-C? The I/O kit fundamentals section on Apple's dev site tells me that a driver gets notified of an idle / system sleep, but I can't find a way of preventing the system from sleeping. Is it even possible?
I've come across some other solutions using Caffeine, jiggler, sleepless and even AppleScript, but I want to do this in Objective-C. Thanks.
Here is the official Apple documentation (including code snippet):
Technical Q&A QA1340 - How to I prevent sleep?
Quote: Preventing sleep using I/O Kit in Mac OS X 10.6 Snow Leopard:
#import <IOKit/pwr_mgt/IOPMLib.h>
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
// reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess)
{
// Add the work you need to do without
// the system sleeping here.
success = IOPMAssertionRelease(assertionID);
// The system will be able to sleep again.
}
For older OSX version, check the following:
Technical Q&A QA1160 - How can I prevent system sleep while my application is running?
Quote: Example usage of UpdateSystemActivity (the canonical way for < 10.6)
#include <CoreServices/CoreServices.h>
void
MyTimerCallback(CFRunLoopTimerRef timer, void *info)
{
UpdateSystemActivity(OverallAct);
}
int
main (int argc, const char * argv[])
{
CFRunLoopTimerRef timer;
CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL };
timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, MyTimerCallback, &context);
if (timer != NULL) {
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
}
/* Start the run loop to receive timer callbacks. You don't need to
call this if you already have a Carbon or Cocoa EventLoop running. */
CFRunLoopRun();
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
return (0);
}
Apple's Q&A1340 replaces Q&A1160. The latest Q&A answers the question "Q: How can my application get notified when the computer is going to sleep or waking from sleep? How do I prevent sleep?"
Listing 2 of Q&A1340:
#import <IOKit/pwr_mgt/IOPMLib.h>
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
//reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess)
{
//Add the work you need to do without
// the system sleeping here.
success = IOPMAssertionRelease(assertionID);
//The system will be able to sleep again.
}
Note that you can only stop idle time sleep, not sleep triggered by the user.
For applications supporting Mac OS X 10.6 and later, use the new IOPMAssertion family of functions. These functions allow other applications and utilities to see your application's desire not to sleep; this is critical to working seamlessly with third party power management software.
Just create an NSTimer that fires a function with this
UpdateSystemActivity(OverallAct);
I'm pretty sure that that's exactly what Caffeine does.

Best way to know if application is inactive in cocoa mac OSX?

So, i am building a program that will stand on a exhibition for public usage, and i got a task to make a inactive state for it. Just display some random videos from a folder on the screen, like a screensaver but in the application.
So what is the best and proper way of checking if the user is inactive?
What i am thinking about is some kind of global timer that gets reset on every user input and if it reaches lets say 1 minute it goes into inactive mode. Are there any better ways?
You can use CGEventSourceSecondsSinceLastEventType
Returns the elapsed time since the last event for a Quartz event
source.
/*
To get the elapsed time since the previous input event—keyboard, mouse, or tablet—specify kCGAnyInputEventType.
*/
- (CFTimeInterval)systemIdleTime
{
CFTimeInterval timeSinceLastEvent = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType);
return timeSinceLastEvent;
}
I'm expanding on Parag Bafna's answer. In Qt you can do
#include <ApplicationServices/ApplicationServices.h>
double MyClass::getIdleTime() {
CFTimeInterval timeSinceLastEvent = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType);
return timeSinceLastEvent;
}
You also have to add the framework to your .pro file:
QMAKE_LFLAGS += -F/System/Library/Frameworks/ApplicationServices.framework
LIBS += -framework ApplicationServices
The documentation of the function is here
I've found a solution that uses the HID manager, this seems to be the way to do it in Cocoa. (There's another solution for Carbon, but it doesn't work for 64bit OS X.)
Citing Daniel Reese on the Dan and Cheryl's Place blog:
#include <IOKit/IOKitLib.h>
/*
Returns the number of seconds the machine has been idle or -1 on error.
The code is compatible with Tiger/10.4 and later (but not iOS).
*/
int64_t SystemIdleTime(void) {
int64_t idlesecs = -1;
io_iterator_t iter = 0;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOHIDSystem"),
&iter) == KERN_SUCCESS)
{
io_registry_entry_t entry = IOIteratorNext(iter);
if (entry) {
CFMutableDictionaryRef dict = NULL;
kern_return_t status;
status = IORegistryEntryCreateCFProperties(entry,
&dict,
kCFAllocatorDefault, 0);
if (status == KERN_SUCCESS)
{
CFNumberRef obj = CFDictionaryGetValue(dict,
CFSTR("HIDIdleTime"));
if (obj) {
int64_t nanoseconds = 0;
if (CFNumberGetValue(obj,
kCFNumberSInt64Type,
&nanoseconds))
{
// Convert from nanoseconds to seconds.
idlesecs = (nanoseconds >> 30);
}
}
CFRelease(dict);
}
IOObjectRelease(entry);
}
IOObjectRelease(iter);
}
return idlesecs;
}
The code has been slightly modified, to make it fit into the 80-character limit of stackoverflow.
This might sound like a silly question; but why not just set up a screensaver, with a short fuse?
You can listen for the NSNotification named #"com.apple.screensaver.didstart" if you need to do any resets or cleanups when the user wanders away.
Edit: You could also set up the screen saver; wait for it to fire, and then do your own thing when it starts, stopping the screen saver when you display your own videos; but setting up a screen saver the proper way is probably a good idea.
Take a look at UKIdleTimer, maybe it's what you're looking for.