The Context
Recently, I have been looking for reliable ways to control a secured Android Keyguard. Mainly to display a custom lock screen. I know that Google had stated custom lock screens are not officially supported by the platform and should expect things to break, however, with the existing APIs, I believe there must be ways to do this. I have done tons of research for about a week but still having problem here and there. What I have implemented, assuming a secured Keyguard is enabled, so far are,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED allows an activity(a window) to be displayed on screen on, putting the Keyguard behind, and all unsafe actions are prevented. Notification panel is disabled, finishing the activity will bring up the Keyguard. I implemented as following in my lock screen activity.
@Override public void onAttachedToWindow() { window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); }
KeyguardManager
,KeyguardManager.KeyguardLock
are deprecated classes, but they still work all the way to Jelly Bean. To do this, I have aService
that handles two things, holding a staticKeyguardManager
and the related objects, and have it hold aBroadcastReceiver
to receiveIntent.ACTION_SCREEN_ON
andIntent.ACTION_SCREEN_OFF
. (all the objects are initialized properly)
For ScreenReceiver
public static synchronized void disableKeyguard() {
if ( isLocked ) {
if ( keyguardLock == null ) {
keyguardLock = keyguardManager.newKeyguardLock(LOG_TAG);
}
keyguardLock.disableKeyguard();
isLocked = false;
}
}
public static synchronized void reenableKeyguard() {
if ( !isLocked ) {
if ( keyguardLock == null ) {
keyguardLock = keyguardManager.newKeyguardLock(LOG_TAG);
}
keyguardLock.reenableKeyguard();
keyguardLock = null;
isLocked = true;
}
}
For BroadcastReceiver
@Override
public void onReceive( Context context, Intent intent ) {
if ( intent.getAction().equals(Intent.ACTION_SCREEN_ON) ) {
Intent start = new Intent(context, LockScreen.class);
start.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(start);
} else if ( intent.getAction().equals(Intent.ACTION_SCREEN_OFF) ) {
ScreenReceiverService.reenableKeyguard();
}
}
For LockScreenActivity
, when the user had input the correct passcode,
window.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
ScreenReceiverService.disableKeyguard();
finish();
The Problem
Things that works
ACTION_ON
andACTION_OFF
are received reliably.- LockScreenActivity is shown before the Keyguard (without telephone state handling yet)
- Notification cannot be pulled down, exiting the activity in any way would display the lockscreen.
Things that does not work
- After I disable
Keyguard
and call finish(), my app exits and homescreen or the last activity before the screen went off is shown. However, whenever I press the Home Key, the Keyguard will flash into the screen, quickly dismissing itself immediately, and the normal Home Key function/event is not handled (will not return to homescreen after flashing). This is observed when I rapidly tapped the Home Key repeatedly.
- After I disable
I even looked into the Android source code to find out the Home Key handling, but it is never sent to third-party applications unless the window type is WindowManager.LayoutParams.TYPE_KEYGUARD
or WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
, which will throw SecurityException on 4.0+ even it worked on earlier platforms. And for the Keyguard
, I have declared DISABLE_KEYGUARD
permission use this shouldn't be the problem. My guess is the flag FLAG_SHOW_WHEN_LOCKED
will tell the system to handle to Keyguard
in some ways that would conflict with other disable calls. Since this flag is mostly used for Alarm/SMS type application, which is to show limited information to the user, then dismiss themselves and bring up the Keyguard
. But in my case, having the user unlock my lock screen then unlock the system lockscreen simply defeats the purpose of my app.
So the question is why would the Keyguard
flashes whenever I press Home after I disabled it? Is there any workaround/solution for this issue?
P.S. Thank you for reading such a long question. This is my first time asking a question here, if there is anything that I did wrong, please tell me (i.e. format, grammar, code convention, tags, etc.). Also I had no experience with any programming knowledge, I started with Android before I know what Java is. So I have not taken any proper course/training yet, this community is awesome and often help people like I even if they are simple questions, and of course watching Google I/O videos, reading blogs, read others' code help me a lot. So please tolerate any dumb mistakes/obvious bugs/stupid questions. I am only 16. ^_^"
In your LockScreenActivity, ending the validation code by
finish();
kills the LockscreenActivity and thus the whole app. Instead of that, you could just launch back your main activity (or any other) like this :I tried to get this but my activity always preceded by the system lock screen. isOrderdBroadcast() says that ACTION_SCREEN_NO is an ordered broadcast.
I added flag to the activity :
in onAttachedView(). But still the system lock is getting the preference over my Custom screen lock activity.
How did you get your activity before the system lock screen?
EDIT On a hindsight, I think my understanding of the lock screen concept was wrong. My broadcast receiver was getting the broadcast first. But what was showing before that was the system lock screen launched when SCREEN_OFF is received. Fixed that problem as of now.
But stumped by the ambiguity of home button behavior. This won't be a problem in post ICS devices as all hard buttons are discouraged.
I had the same problem for the click of HOME button while unlocking the device. This can be solved by reseting the password to blank (ie "") :
and then disabling the keygaurd :
Hope this solved your problem. \m/ keep coding!
I have used this with some success in both Gingerbread and ICS to open my activity (via a background service which is starting it). In the activity being started:
If AOSP is in your control then you need to set the simple flag and keyguard() is gone for good. Here is the details to do that, get into the file "overlay/frameworks/base/packages/SystemUI/res/values/config.xml" and search for "config_enableKeyguardService" then set the flag to false.
NO MORE keyGuard, pheww