Search code examples
androidlockscreenkiosk-mode

Android lock screen multiple activities (a.k.a basic kiosk mode)


I try to implement a kiosk mode application. I was able to lock down most of the possibilities to close the app or access system functions. Now, I was wondering, if it is possible have multiple activities in a lock screen. If I switch between multiple activities of my app, the default lock screen is shown for a short moment and then the app re-appears. If I just replace fragments, the app works like a charm.

I have the following code:

@Override
public void onAttachedToWindow() {
    getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG|WindowManager.LayoutParams.FLAG_FULLSCREEN);
    super.onAttachedToWindow();
}

and:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}

Does anyone has some hints how to improve the lock down without flickering?


Solution

  • Ok, this is how I solved the problem with kiosk mode in front of the keyguard.

    First of all, I had to accept that the flag FLAG_SHOW_WHEN_LOCKED does not work well with multiple activities. So, we have to reduce the app to one single activity. But this implies another drawback: startActivity will still start new activities and causes the app to flicker.

    To avoid that, I rewrote all Activities and made them Fragments. The MainActivity now keeps control over replacing the fragments when needed. It is declared in the Manifest as SingleTop:

    <activity android:name="com.example.DashboardActivity"
            android:screenOrientation="landscape"
            android:launchMode="singleTop"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.HOME"/>
            </intent-filter>
            ...
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <data android:scheme="video" />
                <category android:name="android.intent.category.BROWSABLE"/>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            ...
        </activity>
    

    The intents are handled with:

    @Override
    public void onNewIntent(Intent intent){
        super.onResume();
        dispatchIntent(intent);
    }
    
    public void dispatchIntent(Intent intent) {
        Log.d(TAG, "Intent:" + intent);
    
        Bundle extras = intent.getExtras();
        String action = intent.getAction();
        if(extras == null) { 
            extras = new Bundle();
        }
        Fragment fragment = null;
    
        if (Intent.ACTION_VIEW.equals(action)) {
    
            extras.putParcelable("uri", intent.getData());
            fragment = new VideoplayerFragment();
    
        } else {
    
            fragment = new DashboardFragment();
    
        }
    
        addOrReplaceFragment(fragment, extras);
    }
    
    private void addOrReplaceFragment(Fragment fragment, Bundle arguments) {
        if (fragment != null && findViewById(CONTENT_CONTAINERVIEW_ID) != null) {
            if(arguments != null) {
                fragment.setArguments(arguments);
            }
    
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            String tag = fragment.getClass().getSimpleName();
    
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
              .addToBackStack(tag);
    
            if(getFragmentManager().findFragmentByTag(fragment.getClass().getSimpleName()) != null) {
                ft.replace(CONTENT_CONTAINERVIEW_ID, fragment, tag);
            } else {
                ft.add(CONTENT_CONTAINERVIEW_ID, fragment, tag);
            }
    
            ft.commit();
    
        }
    }
    

    Additionally, you should register for SCREEN_ON and SCREEN_OFF intents to launch the activity, if it was stopped due any reason.

    Hope it helps someone.