How to Build a Custom Android Lock Screen with Swipe, Immersive Mode, and Fingerprint Unlock
This article explains why a custom lock screen is useful, describes the core principles of implementing one on Android, and provides step‑by‑step code for broadcast registration, activity configuration, key event blocking, swipe‑to‑unlock logic, immersive and translucent UI flags, and fingerprint integration.
1. Why a Custom Lock Screen Is Needed
Lock screens have existed since the monochrome‑screen era and remain important today, especially with touch devices. Their core purposes are privacy protection, preventing accidental actions, and saving power without closing system apps. While stock lock screens satisfy these needs, a custom lock screen can streamline user interactions, such as changing music tracks with fewer steps and displaying lyrics.
2. Basic Principles of a Custom Lock Screen
Implementing a custom lock screen is complex because Android provides many ways to prevent non‑system lock screens. The typical approach is to start a Service when the app launches, listen for the Intent.ACTION_SCREEN_OFF broadcast, and launch a lock‑screen Activity on top of the system UI when the screen turns off. The activity can hide the system lock screen (except when a password is set).
1. Broadcast Registration
The service must register the SCREEN_OFF broadcast dynamically; static registration in AndroidManifest.xml will not receive it. Example registration code:
IntentFilter mScreenOffFilter = new IntentFilter();
mScreenOffFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenOffReceiver, mScreenOffFilter);
private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Intent lockIntent = new Intent(context, LockScreenActivity.class);
lockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(lockIntent);
}
}
};When starting the activity from a service, FLAG_ACTIVITY_NEW_TASK is required to avoid the "Calling startActivity() from outside of an Activity" exception. Adding FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS is optional.
2. Activity Configuration
The lock‑screen activity must be configured to appear over the system lock screen. The older KeyguardManager method is deprecated; the modern approach uses window flags:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);Declare the permission in the manifest:
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>3. Blocking Physical Keys
To make the custom lock screen robust, block Back and Menu keys by overriding onKeyDown:
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_MENU:
return true; // consume the event
}
return super.onKeyDown(keyCode, event);
}Home and Recent keys are handled at the framework level and are generally left untouched.
4. Swipe‑to‑Unlock Logic
A custom UnderView placed beneath the main lock‑screen view intercepts touch events. When the user swipes horizontally, the view follows the finger; releasing the finger triggers an animation that either slides the view off‑screen (unlock) or returns it to the start position.
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
onAnimationEnd();
break;
case MotionEvent.ACTION_MOVE:
handleMoveView(x);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
doTriggerEvent(x);
break;
}
return true;
}
private void handleMoveView(float x) {
float moveX = x - mStartX;
if (moveX < 0) moveX = 0;
mMoveView.setTranslationX(moveX);
float width = (float) mWidth;
if (getBackground() != null) {
getBackground().setAlpha((int) ((width - mMoveView.getTranslationX()) / width * 200));
}
}
private void doTriggerEvent(float x) {
float moveX = x - mStartX;
if (moveX > mWidth * 0.4f) {
moveMoveView(mWidth - mMoveView.getLeft(), true);
} else {
moveMoveView(-mMoveView.getLeft(), false);
}
}
private void moveMoveView(float to, boolean exit) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mMoveView, "translationX", to);
animator.addUpdateListener(animation -> {
if (getBackground() != null) {
getBackground().setAlpha((int) (((float) mWidth - mMoveView.getTranslationX()) / mWidth * 200));
}
});
animator.setDuration(250).start();
if (exit) {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mainHandler.obtainMessage(LockScreenActivity.MSG_LAUNCH_HOME).sendToTarget();
super.onAnimationEnd(animation);
}
});
}
}3. Transparent Bars and Immersive Mode
Immersive mode hides both the status bar and navigation bar while allowing a short swipe to reveal them temporarily. The relevant flags (available from Android 4.4) are:
View.SYSTEM_UI_FLAG_IMMERSIVE View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY View.SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_HIDE_NAVIGATION View.SYSTEM_UI_FLAG_LAYOUT_STABLE View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATIONApply them in onCreate and again in onWindowFocusChanged to ensure they stay active:
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
}For transparent status and navigation bars, use the following code depending on the Android version:
// Android 4.4 (KitKat) – translucent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
// Android 5.0 (Lollipop) and above – fully transparent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0); // transparent
}4. Fingerprint Unlock Integration
When a device with fingerprint authentication unlocks the system lock screen, the custom lock screen may remain visible. To handle this, listen for Intent.ACTION_USER_PRESENT and finish the custom lock‑screen activity only when a lock‑screen password is set.
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (km.isKeyguardSecure()) {
Intent i = new Intent(NOTIFY_USER_PRESENT);
context.sendBroadcast(i);
}
}
}Additionally, embed fingerprint authentication directly in the lock‑screen activity for devices where the system fingerprint UI is unavailable:
private void startFingerPrintListening() {
if (!isFingerprintAuthAvailable()) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) {
mFingerprintManager.authenticate(null, mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {
@Override public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { finish(); }
@Override public void onAuthenticationError(int errorCode, CharSequence errString) { super.onAuthenticationError(errorCode, errString); }
@Override public void onAuthenticationFailed() { super.onAuthenticationFailed(); }
}, null);
}
}
}
public boolean isFingerprintAuthAvailable() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mKeyguardManager = (KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE);
if (!mKeyguardManager.isKeyguardSecure()) return false;
if (checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) {
mFingerprintManager = (FingerprintManager) getSystemService(Activity.FINGERPRINT_SERVICE);
mCancellationSignal = new CancellationSignal();
return mFingerprintManager.isHardwareDetected() && mFingerprintManager.hasEnrolledFingerprints();
}
}
return false;
}Declare the fingerprint permission in the manifest:
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>5. Summary
Creating a custom Android lock screen involves starting a service that reacts to screen‑off events, launching an activity with appropriate window flags, handling key events, implementing swipe‑to‑unlock logic, combining immersive and translucent UI modes, and optionally integrating fingerprint authentication. Paying attention to these technical details results in a smooth, user‑friendly lock‑screen experience.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
