Is there a away to detect the event when iOS device goes to sleep mode (when the screen gets blackened)?
Asked Answered
D

2

14

I wanted to detect two events :

  1. Device gets locked/unlocked.
  2. Device goes to sleep and the screen blackens.

First one I have been able to achieve here: Is there a way to check if the iOS device is locked/unlocked?

Now I want to detect the second event, is there any way to do it ?

Deadlight answered 16/1, 2013 at 6:0 Comment(2)
Is this question about private apis for iPhone?Grappa
Anything will suffice, I don't intend to submit my app on App Store.Deadlight
C
22

You basically already have the solution, which I'm guessing you found from one of my recent answers :)

Use the com.apple.springboard.hasBlankedScreen event.

There are multiple events that occur when the screen blanks, but this one should suffice:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                NULL, // observer
                                hasBlankedScreen, // callback
                                CFSTR("com.apple.springboard.hasBlankedScreen"), // event name
                                NULL, // object
                                CFNotificationSuspensionBehaviorDeliverImmediately);

where the callback is:

static void hasBlankedScreen(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSString* notifyName = (__bridge NSString*)name;
    // this check should really only be necessary if you reuse this one callback method
    //  for multiple Darwin notification events
    if ([notifyName isEqualToString:@"com.apple.springboard.hasBlankedScreen"]) {
       NSLog(@"screen has either gone dark, or been turned back on!");
    }
}

Update: as @VictorRonin said in his comment below, it should be easy to keep track yourself whether the screen is currently on or off. That allows you to determine whether the hasBlankedScreen event is occurring when the screen goes on or off. For example, when your app starts, set a variable to indicate that the screen is on. Also, any time any UI interaction occurs (button pressed, etc.), you know the screen must currently be on. So, the next hasBlankedScreen you get should indicate that the screen is off.

Also, I want to make sure we're clear on the terminology. The device locks when the screen automatically darkens due to a timeout, or when the user manually presses the power button. This happens regardless of whether the user has a Passcode configured. At that time, you will see the com.apple.springboard.hasBlankedScreen and the com.apple.springboard.lockcomplete events.

When the screen turns back on, you will see com.apple.springboard.hasBlankedScreen once again. But, you will not see com.apple.springboard.lockstate until the user has actually unlocked the device with a swipe (and maybe a passcode).


Update 2:

There's yet another way to do this. You can use an alternate set of APIs to listen for this notification, and also get a state variable when the notification comes:

#import <notify.h>

int status = notify_register_dispatch("com.apple.springboard.hasBlankedScreen",
                                      &notifyToken,
                                      dispatch_get_main_queue(), ^(int t) {
                                          uint64_t state;
                                          int result = notify_get_state(notifyToken, &state);
                                          NSLog(@"lock state change = %llu", state);
                                          if (result != NOTIFY_STATUS_OK) {
                                              NSLog(@"notify_get_state() not returning NOTIFY_STATUS_OK");
                                          }
                                      });
if (status != NOTIFY_STATUS_OK) {
    NSLog(@"notify_register_dispatch() not returning NOTIFY_STATUS_OK");
}

and you will need to keep an ivar, or some other persistent variable, to store the notification token (do not just make this a local variable in the method that registers!)

int notifyToken;

You should see the state variable, obtained via notify_get_state(), toggle between 0 and 1, which will let you distinguish between screen on and off events.

Although this document is very old, it does list which notification events have an associated state that can be retrieved via notify_get_state().

Warning: see this related question for some complications with this last technique

Cottony answered 16/1, 2013 at 11:39 Comment(14)
I have already tried this, turns out, this notification comes in both the scenarios, when device goes to sleep and when device awakes from the sleep.: Jan 16 18:48:53 unknown SpringBoard[15] <Notice>: Posting 'com.apple.iokit.hid.displayStatus' notifyState=0 Jan 16 18:48:53 unknown Myapp[1921] <Warning>: Darwin notification NAME = com.apple.springboard.hasBlankedScreen an 16 18:50:29 unknown SpringBoard[15] <Notice>: Posting 'com.apple.iokit.hid.displayStatus' notifyState=1 Jan 16 18:50:29 unknown Myapp[1921] <Warning>: Darwin notification NAME = com.apple.springboard.hasBlankedScreenDeadlight
You can keep a state variable, whether a device is asleep now or awake.Harass
Do I notice correctly that my app will not receive lockstate or lockcomplete notifications if it is already in the background when the lock action occurs?Fain
@SpencerWilliams, not necessarily. If you just press Home to background your app, assuming your app .plist does not specify that it exits on suspend, then you will get the notifications. Now, as with anything, there are limits on how much time your app gets in the background, so you may have to fiddle with things if you want to get these notifications indefinitely. That's really another question, and depends on whether we're talking about a jailbreak app or not. But, fundamentally, Darwin notifications don't stop just because you've entered the background.Cottony
@SpencerWilliams, also take careful note of the last parameter I pass when I register for the notifications. If you aren't using CFNotificationSuspensionBehaviorDeliverImmediately, then you very well might not see notifications when your app is in the background.Cottony
I am using CFNotificationSuspensionBehaviorDeliverImmediately, but it seems my app doesn't receive the notifications if it's not in the foreground when the phone is locked, ie if another app is in use and the lock button is pressed, my app doesn't know the device is now locked (without polling periodically)Fain
@SpencerWilliams, hmmm. Works for me. I think you'd probably need to post a new question, and fully list your code, and use case description. Thanks.Cottony
@Everybody: Please look at this question, because it explains some potential pitfalls with this approach: #14820749Harass
@nate : Is there any public API to achieve the same?Ludicrous
@SunilAdhyaru, The answer above really is using Public APIs. CFNotificationCenterAddObserver() is public on iOS. The string name of the event is undocumented. But, it's not actually private. You should be able to use this code in an App Store app. I haven't tested it in a while though, so make sure it still woriks.Cottony
@mcfedr, these APIs are public, so you should be fine with app store approval. The string com.apple.springboard.hasBlankedScreen is undocumented, which is different than being a private API. So, iOS could change and it could stop working in a future release. But, obviously, it's been there for years now.Cottony
Apple App Store rule changed in 2018 and now rejects apps that use com.apple.springboard.hasBlankedScreen. Has anyone found a workaround?Poland
@WilliamGrand this question was explicitly not for the App Store, but your comment is a good one for others. If someone was so inclined, obfuscation might get around that "problem" ;)Cottony
This is not allowed by apple anymore. forums.developer.apple.com/thread/76608Tremulous
H
1

You can also subscribe for a notification: "com.apple.springboard.lockstate" and use API SBGetScreenLockStatus to determine status whether device is locked or not.

Harass answered 16/1, 2013 at 16:33 Comment(4)
How to call and use it? actually I only want to detect the event when the screen blackens i.e. screen transitions from on/off. Thanks in advance.Deadlight
Info: Its not for a jailbroken device.Deadlight
First notification is used with the same code, which Nate wrote. SBGetScreenLockStatus is described in linked article. These events I added as an answer for "Device gets locked/unlocked". And it works on non jailbroken device.Harass
@Everybody: Please look at this question, because it explains some potential pitfalls with this approach: #14820749Harass

© 2022 - 2024 — McMap. All rights reserved.