How to write simple Android Lock Screen

Hello everyone,

I will start this blog by explaining how you can write simple Lock Screen for Android.

First, it important to understand that officially, Android have built it Lock Screen mechanism, and there isn’t any official API for bypass or replace this Lock Screen.
However, if you want to do that nevertheless, you can use some tricks that can help you achieve your purpose. I hope this post will help other programmers as I know I wasn’t able to find too much information when I tried to build Lock Screen from scratch.

The Lock Screen I’m going to show is quite a real one, as it disables completely the user ability to communicate with the device as long as the Lock Screen is displayed.
In order to do that, I needed to find a way to bypass Home Screen button, and also hide the Status Bar.
We will use very simple UI for this demo:


The basic concept for building your own Lock Screen is to be able to draw your layout on top of any other component in the user device so the user won’t be able to use the device unless he will follow your own security demands, like type in a PIN or swipe left, or any other measure you choose to implement.

So all you need to do is run your Service that will listen to any Screen Off events, and each time this event happens you will push your UI on top of everything.

 

First, there is two Permission must be including in the Manifest.xml file:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

android.permission.SYSTEM_ALERT_WINDOW permission enables you to draw your layout on top of everything in the device.

android.permission.RECEIVE_BOOT_COMPLETED permission needed in order to run your service right after user switch on his device.

Now, the Service need to listen to Intent.ACTION_SCREEN_OFF event. So after the Service will start we will create and register simple BroadcastReciever inside the Service:

BroadcastReceiver screenReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF) && linearLayout == null) {
            init();
        }
    }
};

linearLayout == null is the indicator to check if our layout is already displayed, as we don’t want to draw duplicate layouts again and again.

We will register this receiver in the Service onCreate() method, and will define the LayoutParams we will use for drawing our layout on top of everything:

private LinearLayout linearLayout;
private WindowManager.LayoutParams layoutParams;
private WindowManager windowManager;

@Override
public void onCreate() {
 super.onCreate();
 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
 registerReceiver(screenReceiver, intentFilter);

 windowManager = ((WindowManager) getSystemService(WINDOW_SERVICE));
 layoutParams = new WindowManager.LayoutParams(
 WindowManager.LayoutParams.MATCH_PARENT,
 WindowManager.LayoutParams.MATCH_PARENT,
 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 
 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,
 PixelFormat.TRANSLUCENT);
}

WindowManager.LayoutParams.TYPE_SYSTEM_ERROR is a window flag that gives us the ability to put our layout on top of everything.
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN simply makes our layout to be drawn on the entire screen.
WindowManager.LayoutParams.SYSTEM_UI_FLAG_HIDE_NAVIGATION hide the device navigation buttons.

Pay attention: we must target targetSdkVersion not higher than 22! otherwise will get Permission exception.

So right now, each time screen off event will be intercepted by our screenReceiver and linearlayout will be null, we will call our init() method:

private void init() {
    linearLayout = new LinearLayout(this);
    windowManager.addView(linearLayout, layoutParams);
    ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(R.layout.lock_screen, linearLayout);
    View btnClose = linearLayout.findViewById(R.id.btn_close);
    btnClose.setOnClickListener(this);
}

lock_screen.xml layout is very simple and contains just a simple button:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent">

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="close"
        android:layout_gravity="center"/>

</FrameLayout>

Now, all left is to remove all UI component after the user clicked our botton:

@Override
public void onClick(View view) {
    windowManager.removeView(linearLayout);
    linearLayout = null;
}

MainActivity.class will be super simple. It will just trigger the service.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, LockScreenService.class));
        finish();
    }
}

Hope you enjoyed! The whole code for this demo can be found here:
http://github.com/yshahak/LockScreenDemo

Leave a Reply

Your email address will not be published. Required fields are marked *