web-dev-qa-db-fra.com

Lock Unlock events iphone

Comment détecter les événements de verrouillage/déverrouillage sur l'iPhone? En supposant que cela n'est possible que pour les appareils jailbreakés, pouvez-vous me diriger vers la bonne API?

Par événements de verrouillage, je veux dire afficher ou masquer l'écran de verrouillage (qui pourrait avoir besoin d'un mot de passe pour se déverrouiller ou non).

35
Panos

Vous pouvez utiliser notifications Darwin , pour écouter les événements. D'après mes tests sur un iPhone 4 iOS 5.0.1 jailbreaké, je pense qu'un de ces événements pourrait être ce dont vous avez besoin:

com.Apple.springboard.lockstate
com.Apple.springboard.lockcomplete

Remarque: selon l'affiche commentaires à une question similaire à laquelle j'ai répondu ici , cela devrait fonctionner sur un téléphone non jailbreaké, aussi.

Pour l'utiliser, enregistrez-vous pour l'événement comme ceci (cela ne s'inscrit que pour le premier événement ci-dessus, mais vous pouvez également ajouter un observateur pour lockcomplete):

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                (void*)self, // observer (can be NULL)
                                lockStateChanged, // callback
                                CFSTR("com.Apple.springboard.lockstate"), // event name
                                NULL, // object
                                CFNotificationSuspensionBehaviorDeliverImmediately);

lockStateChanged est votre rappel d'événement:

static void lockStateChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
    NSLog(@"event received!");
    if (observer != NULL) {
        MyClass *this = (MyClass*)observer;
    }

    // you might try inspecting the `userInfo` dictionary, to see 
    //  if it contains any useful info
    if (userInfo != nil) {
        CFShow(userInfo);
    }
}

L'événement lockstate se produit lorsque le périphérique est verrouillé et déverrouillé, mais l'événement lockcomplete n'est déclenché que lorsque le périphérique serrures. Une autre façon de déterminer si l'événement concerne un événement de verrouillage ou de déverrouillage consiste à utiliser notify_get_state(). Vous obtiendrez une valeur différente pour verrouiller/déverrouiller, comme décrit ici .

22
Nate

Autour de la réponse:

L'application démissionnera active sera appelée dans toutes sortes de scénarios ... et d'après tous mes tests, même si votre application reste éveillée en arrière-plan, il n'y a aucun moyen de déterminer que l'écran est verrouillé (la vitesse du processeur ne fait pas de rapport, la vitesse du BUS reste le même, le nom/numéro mach_time ne change pas) ...

Cependant, il semble Apple éteint l'accéléromètre lorsque l'appareil est verrouillé ... Activer l'accéléromètre iPhone lorsque l'écran est verrouillé (iOS4.2 testé sur iPhone 4 a Ce comportement)

Donc...

Dans votre délégué d'application:

- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"STATUS - Application will Resign Active");
    // Start checking the accelerometer (while we are in the background)
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:1]; // Ping every second
    _notActiveTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(deviceDidLock) userInfo:nil repeats:NO]; // 2 seconds for wiggle

}
//Deprecated in iOS5
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    NSLog(@"STATUS - Update from accelerometer");
    [_notActiveTimer invalidate];
    _notActiveTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(deviceDidLock) userInfo:nil repeats:NO];
}

- (void)deviceDidLock
{
    NSLog(@"STATUS - Device locked!");
    [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
    _notActiveTimer = nil;
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"STATUS - Application did become active");
    [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
    [_notActiveTimer invalidate];
    _notActiveTimer = nil;
}

Je sais ... C'est une sorte de piratage, mais cela a fonctionné comme un charme pour moi jusqu'à présent. Veuillez mettre à jour si vous voyez des problèmes qui empêchent cela de fonctionner.

13
BadPirate

Il existe une meilleure façon de distinguer le changement de tâche et le verrouillage d'écran d'origine applicationWillResignActive: rappels qui n'impliquent même pas de fonctionnalités non documentées telles que l'état de l'accéléromètre.

Lorsque l'application se déplace en arrière-plan, le délégué de l'application reçoit d'abord un applicationWillResignActive:, puis un applicationDidEnterBackground:. Lorsque l'application est interrompue en appuyant sur le bouton Verrouiller ou par un appel téléphonique entrant, cette dernière méthode n'est pas appelée. Nous pouvons utiliser ces informations pour distinguer les deux scénarios.

Supposons que vous souhaitiez être rappelé dans la méthode screenLockActivated si l'écran est verrouillé. Voici la magie:

- (void)applicationWillResignActive:(UIApplication*)aApplication
{
    [self performSelector:@selector(screenLockActivated)
               withObject:nil
               afterDelay:0];
}

- (void)applicationDidEnterBackground:(UIApplication*)aApplication
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
}

- (void)screenLockActivated
{
    NSLog(@"yaay");
}

Explication:

Par défaut, nous supposons que chaque appel à applicationWillResignActive: est dû à une transition d'état active-> inactive (comme lors du verrouillage de l'écran) mais nous avons généreusement laissé le système prouver le contraire dans un délai (dans ce cas, un cycle de boucle unique) en retardant l'appel à screenLockActivated. Dans le cas où l'écran se verrouille, le système termine le cycle de boucle en cours sans toucher à aucune autre méthode déléguée. Si, cependant, il s'agit d'une transition d'état actif-> en arrière-plan, elle appelle également applicationDidEnterBackground: avant la fin du cycle, ce qui nous permet d'annuler simplement la demande précédemment planifiée à partir de là, l'empêchant ainsi d'être appelée lorsqu'elle n'est pas supposée le faire.

Prendre plaisir!

7
Lvsti

Au moment de l'écriture, il existe deux moyens assez fiables pour détecter le verrouillage de l'appareil:


Protection des données

En activant le droit à la protection des données votre application peut s'abonner au applicationProtectedDataWillBecomeUnavailable: et applicationProtectedDataDidBecomeAvailable: notifications pour déterminer avec une probabilité élevée lorsqu'un appareil qui utilise l'authentification par mot de passe/TouchID est verrouillé/déverrouillé. Pour déterminer si un appareil utilise un code d'accès/TouchID LAContext peut être interrogé.

Mises en garde : cette méthode repose sur le fait que les "données protégées deviennent indisponibles" coïncidant avec le verrouillage du téléphone. Lorsque le téléphone utilise TouchID et que le bouton veille/verrouillage est enfoncé, le téléphone est verrouillé, les données protégées deviennent indisponibles et un code d'accès sera immédiatement requis pour le déverrouiller à nouveau. Cela signifie que les données protégées devenant indisponibles indiquent essentiellement que le téléphone a été verrouillé. Ce n'est pas nécessairement vrai lorsque quelqu'un utilise juste un mot de passe car il peut définir le temps "nécessite un mot de passe" à n'importe où de immédiatement vers quelque chose comme 4 heures. Dans ce cas, le téléphone indiquera qu'il est capable de gérer des données protégées, mais le verrouillage du téléphone n'entraînera pas l'indisponibilité des données protégées pendant un certain temps.


Calendrier du cycle de vie

Si votre application est au premier plan, il y aura un changement notable dans la différence de temps entre les deux événements du cycle de vie UIApplicationWillResignActiveNotification et UIApplicationDidEnterBackgroundNotification selon ce qui les déclenche.

(Cela a été testé dans iOS 10 et pourrait changer dans les futures versions)

Appuyer sur le bouton d'accueil entraîne un délai important entre les deux (même lorsque le paramètre de mouvement réduit est activé):

15:23:42.517 willResignActive
15:23:43.182 didEnterBackground
15:23:43.184 difference: 0.666346

Le verrouillage de l'appareil pendant que l'application est ouverte crée un délai plus trivial (<~ 0,2 s) entre les deux événements:

15:22:59.236 willResignActive
15:22:59.267 didEnterBackground
15:22:59.267 difference: 0.031404
7
Warpling

dans iOS 8, vous verrouillez l'écran ou appuyez sur le bouton d'accueil, tous ces éléments font de l'application Push en arrière-plan, mais vous ne savez pas quel opérateur en résulte. Ma solution est la même avec Nits007ak, utilisez notify_register_dispatch pour obtenir l'état.

#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);
                                 if(state == 0) {
                                     NSLog(@"unlock device");
                                 } else {
                                     NSLog(@"lock device");
                                 }
                             }
                             );

Tant que l'application est en cours d'exécution, au premier plan ou en arrière-plan. pas suspendre, vous pouvez obtenir cet événement.

Et vous pouvez utiliser notify_token comme paramètre de notify_get_state pour obtenir l'état actuel n'importe où, cela est utile lorsque vous voulez connaître l'état et que l'état de l'écran ne change pas.

5
david

Importez simplement #import notify.h avant d'utiliser ce code. prendre plaisir!!

-(void)registerAppforDetectLockState {

    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);

        if(state == 0) {
            NSLog(@"unlock device");
        } else {
            NSLog(@"lock device");
        }

        NSLog(@"com.Apple.springboard.lockstate = %llu", state);
        UILocalNotification *notification = [[UILocalNotification alloc]init];
        notification.repeatInterval = NSDayCalendarUnit;
        [notification setAlertBody:@"Hello world!! I come becoz you lock/unlock your device :)"];
        notification.alertAction = @"View";
        notification.alertAction = @"Yes";
        [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        notification.soundName = UILocalNotificationDefaultSoundName;
        [notification setTimeZone:[NSTimeZone  defaultTimeZone]];

        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

    });
}
4
Nits007ak

Si le code d'accès est défini, vous pouvez utiliser ces événements dans AppDelegate

-(void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application
{
}

- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
{
}
3
Eyal

À partir de nombreux essais et erreurs, la surveillance de l'écran vide, les événements de verrouillage terminé et de verrouillage ont donné un indicateur d'écran de verrouillage cohérent. Vous devrez surveiller une transition d'état.

// call back
void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    // notification comes in order of
    // "com.Apple.springboard.hasBlankedScreen" notification
    // "com.Apple.springboard.lockcomplete" notification only if locked
    // "com.Apple.springboard.lockstate" notification

    AppDelegate *appDelegate = CFBridgingRelease(observer);

    NSString *eventName = (__bridge NSString*)name;
    NSLog(@"Darwin notification NAME = %@",name);

    if([eventName isEqualToString:@"com.Apple.springboard.hasBlankedScreen"])
    {
        NSLog(@"SCREEN BLANK");

        appDelegate.bDeviceLocked = false; // clear
    }
    else if([eventName isEqualToString:@"com.Apple.springboard.lockcomplete"])
    {
        NSLog(@"DEVICE LOCK");

        appDelegate.bDeviceLocked = true; // set
    }
    else if([eventName isEqualToString:@"com.Apple.springboard.lockstate"])
    {
        NSLog(@"LOCK STATUS CHANGE");

        if(appDelegate.bDeviceLocked) // if a lock, is set
        {
            NSLog(@"DEVICE IS LOCKED");
        }
        else
        {
            NSLog(@"DEVICE IS UNLOCKED");
        }
    }
}

-(void)registerforDeviceLockNotif
{
    // screen and lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    CFBridgingRetain(self), // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.hasBlankedScreen"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    CFBridgingRetain(self), // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockcomplete"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    CFBridgingRetain(self), // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockstate"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);
}

Pour que les indicateurs de verrouillage d'écran s'exécutent en arrière-plan, vous devez implémenter un traitement en arrière-plan appelant ce qui suit au lancement de l'application.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.backgroundTaskIdentifier =
    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
    }];

    [self registerforDeviceLockNotif];
}
2
CFreymarc

Si votre application est en cours d'exécution et que l'utilisateur verrouille l'appareil, votre délégué d'application recevra un appel à "l'application démissionnera active:". Si votre application était en cours d'exécution lorsqu'elle était verrouillée, elle recevra un appel à "l'application est devenue active:" lorsque l'appareil est déverrouillé. Mais vous recevez les mêmes appels vers votre application si l'utilisateur reçoit un appel téléphonique et choisit ensuite de l'ignorer. Pour autant que je sache, vous ne pouvez pas faire la différence.

Et si votre application ne s'exécutait à aucun de ces moments, il n'y a aucun moyen d'être averti car votre application ne fonctionne pas.

1
user738960