pantalla - recuperar notificaciones iphone
Bloquear Desbloquear eventos iphone (10)
¿Cómo puedo detectar eventos de bloqueo / desbloqueo en el iPhone? Asumiendo que solo es posible para dispositivos jailbreak, ¿me pueden indicar la API correcta?
Por bloqueo de eventos , me refiero a mostrar u ocultar la pantalla de bloqueo (que podría necesitar una contraseña para desbloquear, o no).
A partir de una gran cantidad de pruebas y errores, descubrió que el monitoreo de la pantalla en blanco, el bloqueo completo y el bloqueo de eventos de estado brinda un indicador de pantalla de bloqueo consistente. Tendrá que controlar una transición de estado.
// 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);
}
Para que los indicadores de bloqueo de pantalla se ejecuten en segundo plano, debe implementar un procesamiento de fondo invocando lo siguiente al iniciarse la aplicación.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
}];
[self registerforDeviceLockNotif];
}
Al momento de escribir, hay dos maneras bastante confiables de detectar el bloqueo del dispositivo:
Protección de Datos
Al habilitar la titularidad de Protección de datos, su aplicación puede suscribirse a la applicationProtectedDataWillBecomeUnavailable:
y applicationProtectedDataDidBecomeAvailable:
notificaciones para determinar con alta probabilidad cuando un dispositivo que utiliza Passcode / TouchID Authentication está bloqueado / desbloqueado. Para determinar si un dispositivo usa una contraseña / TouchID LAContext
se puede consultar.
Ciclo de vida
Si su aplicación está en primer plano, habrá un cambio notable en la diferencia de tiempo entre los dos eventos del ciclo de vida UIApplicationWillResignActiveNotification
y UIApplicationDidEnterBackgroundNotification
dependiendo de qué los desencadena.
(Esto fue probado en iOS 10 y puede cambiar en versiones futuras)
Al presionar el botón de inicio se produce un retraso significativo entre los dos (incluso cuando la configuración de Movimiento reducido está habilitada):
15:23:42.517 willResignActive
15:23:43.182 didEnterBackground
15:23:43.184 difference: 0.666346
Bloquear el dispositivo mientras la aplicación está abierta crea un retraso más trivial (<~ 0.2s) entre los dos eventos:
15:22:59.236 willResignActive
15:22:59.267 didEnterBackground
15:22:59.267 difference: 0.031404
Existe una manera más bonita de separar el cambio de tareas y la applicationWillResignActive:
originada en bloqueo de pantallaWillResignActive applicationWillResignActive:
devoluciones de llamada que ni siquiera involucran funciones no documentadas como el estado del acelerómetro.
Cuando la aplicación se está moviendo a un segundo plano, al delegado de la aplicación se le envía primero una applicationWillResignActive:
, luego una applicationDidEnterBackground:
Cuando se interrumpe la aplicación presionando el botón de Bloqueo o por una llamada de teléfono entrante, no se llama al último método. Podemos usar esta información para distinguir entre los dos escenarios.
Supongamos que desea que se le devuelva la llamada en el método screenLockActivated
si la pantalla se bloquea. Aquí está la magia:
- (void)applicationWillResignActive:(UIApplication*)aApplication
{
[self performSelector:@selector(screenLockActivated)
withObject:nil
afterDelay:0];
}
- (void)applicationDidEnterBackground:(UIApplication*)aApplication
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)screenLockActivated
{
NSLog(@"yaay");
}
Explicación:
De forma predeterminada, suponemos que cada llamada a applicationWillResignActive:
se debe a una transición de estado activo-> inactivo (como cuando se bloquea la pantalla) pero dejamos que el sistema demuestre lo contrario dentro de un tiempo de espera (en este caso, un solo ciclo de ejecución) retrasando la llamada a screenLockActivated
. En caso de que la pantalla se bloquee, el sistema finaliza el ciclo de ciclo de ejecución actual sin tocar ningún otro método delegado. Sin embargo, si se trata de una transición de estado activo-> fondo, también invoca applicationDidEnterBackground:
antes del final del ciclo, lo que nos permite simplemente cancelar la solicitud programada previamente desde allí, evitando así que se llame cuando no se supone que debe .
¡Disfrutar!
La forma más sencilla de obtener eventos de bloqueo y desbloqueo de pantalla es agregar observadores de eventos usando NSNotificationCenter en su controlador de visualización. Agregué el siguiente observador en el método viewdidload. Esto es lo que hice:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationEnteredForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
Luego agregué el siguiente selector al viewcontroller. Se llamará a este selector cuando la pantalla esté desbloqueada.
- (void)applicationEnteredForeground:(NSNotification *)notification {
NSLog(@"Application Entered Foreground");
}
Si desea detectar el evento cuando la pantalla se bloquea, puede reemplazar UIApplicationWillEnterForegroundNotification con UIApplicationDidEnterBackgroundNotification .
Puede usar las notificaciones de Darwin para escuchar los eventos. A partir de mis pruebas en un iPhone 4 con iOS 5.0.1 liberado, creo que uno de estos eventos podría ser lo que necesita:
com.apple.springboard.lockstate
com.apple.springboard.lockcomplete
Nota: de acuerdo con los comentarios del póster a una pregunta similar que respondí aquí , esto también debería funcionar en un teléfono sin jailbreak.
Para usar esto, regístrese para el evento de esta manera (esto se registra solo para el primer evento anterior, pero también puede agregar un observador para lockcomplete
):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
(void*)self, // observer (can be NULL)
lockStateChanged, // callback
CFSTR("com.apple.springboard.lockstate"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
donde lockStateChanged
es su devolución de llamada de evento:
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);
}
}
El evento lockstate
ocurre cuando el dispositivo está bloqueado y desbloqueado, pero el evento lockcomplete
solo se activa cuando el dispositivo se bloquea. Otra forma de determinar si el evento es para un evento de bloqueo o desbloqueo es usar notify_get_state()
. Obtendrá un valor diferente para bloqueo vs desbloqueo, como se describe aquí .
Ronda de respuesta:
La aplicación se cerrará activa se llama en todo tipo de escenarios ... y de todas mis pruebas, incluso si su aplicación permanece encendida mientras está en segundo plano, no hay formas de determinar que la pantalla está bloqueada (velocidad de la CPU no informa, velocidad del BUS sigue siendo el mismo, mach_time denom / numer no cambia) ...
Sin embargo, parece que Apple apaga el acelerómetro cuando el dispositivo está bloqueado ... Habilita el acelerómetro iPhone mientras la pantalla está bloqueada (probado iOS4.2 en iPhone 4 tiene este comportamiento)
Así...
En tu aplicación delegada:
- (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;
}
Lo sé ... Es una especie de truco, pero hasta ahora ha funcionado como un encanto. Actualice si ve algún problema que impida que esto funcione.
Si se establece el código de acceso, puede usar estos eventos en AppDelegate
-(void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application
{
}
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
{
}
Si su aplicación se está ejecutando y el usuario bloquea el dispositivo, su delegado de aplicaciones recibirá una llamada a ''la aplicación se va a renunciar a Active:''. Si su aplicación se estaba ejecutando cuando estaba bloqueada, recibirá una llamada a ''la aplicación Did Become Active:'' cuando el dispositivo esté desbloqueado. Pero recibe las mismas llamadas a su aplicación si el usuario recibe una llamada y luego elige ignorarla. No puedes notar la diferencia hasta donde yo sé.
Y si su aplicación no se estaba ejecutando en ninguno de estos momentos, no hay forma de que se le notifique debido a que su aplicación no se está ejecutando.
Solo importa #import notify.h antes de usar este código. ¡¡disfrutar!!
-(void)registerAppforDetectLockState {
int notify_token;
notify_register_dispatch("com.apple.springboard.lockstate", ¬ify_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];
});
}
en iOS 8, bloqueas la pantalla o presionas el botón de inicio, todos hacen que la aplicación se presione en segundo plano, pero no sabes qué operador resulta en esto. Mi solución es la misma que con Nits007ak, use notify_register_dispatch para obtener el estado.
#import <notify.h>
int notify_token
notify_register_dispatch("com.apple.springboard.lockstate",
¬ify_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");
}
}
);
Siempre que la aplicación se esté ejecutando, en primer plano o en segundo plano. No suspender, puedes obtener este evento.
Y puede usar notify_token como parámetro de notify_get_state para obtener el estado actual en cualquier lugar, esto es útil cuando quiere saber el estado y el estado de la pantalla no cambia.