0

I have an app whose security I would like to improve. When the user unlocks the device I would like the app to return to the login screen so that an intruder cannot use the app just by unlocking the device and having the app resume its original state.

I'm using a BroadcastReceiver to detect when power button is pressed but this currently results the login screen being loaded regardless of whether the user was using the app just before they pressed the power button.

Below I have included my BroadcastReceiver code, perhaps somebody could shed some light on this issue if possible?

public class ScreenReceiver extends BroadcastReceiver {

    public static boolean wasScreenOn = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            Intent n = new Intent(context, MainActivity.class);
            context.startActivity(n);
            wasScreenOn = false;
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            wasScreenOn = true;
        }
    }
}
NobodyTellsMe
  • 113
  • 4
  • 14
  • https://stackoverflow.com/questions/8317331/detecting-when-screen-is-locked – litelite Aug 01 '17 at 18:10
  • Its doing that because you're launching the main activity regardless of what was going on if the screen turns off. You have no logic to differentiate the user having just been using the app. – Gabe Sechan Aug 01 '17 at 18:21

3 Answers3

2

When a user clicks on the home button, onPause() and onStop() gets called. When they click on the back button (to exit the app), onPause(), onStop(), and finally onDestory() gets called. Finally, when they press the power button, onPause() and onStop() gets called.

They all have onPause() being called first. Override onPause() and go back to the main activity using intent. You can also clear the stack of activities so they can't hit the back button to go back into one.

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button goPrivate = (Button) findViewById(R.id.goPrivate);

        goPrivate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, Private.class));
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vzw.www.myapplication.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MAIN"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="GO TO PRIVATE"
        android:id="@+id/goPrivate"/>

</RelativeLayout>

Create a new activity. I named mine Private.java

Private.java

public class Private extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_private);
    }

    @Override
    protected void onPause() {
        super.onPause();
        startActivity(new Intent(Private.this, MainActivity.class));
    }
}

activity_private.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vzw.www.myapplication.Private"
    android:layout_gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PRIVATE!"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

Just create a blank project in Android Studio and try this. It works! :)

What happens if we click into ANOTHER activity and go deep into the app?

Create a boolean inside of Private class.

boolean goSuperClicked = false;

Updated private class is below:

public class Private extends AppCompatActivity {

    Button goSuper;
    boolean goSuperClicked = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_private);

        goSuper = (Button) findViewById(R.id.goSuper);

        goSuper.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                goSuperClicked = true;
                startActivity(new Intent(Private.this, SuperPrivate.class));
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d("onPause()", "onPause called");
        if (!goSuperClicked) {
            startActivity(new Intent(Private.this, MainActivity.class));
        }

    }
}
letsCode
  • 2,774
  • 1
  • 13
  • 37
  • Thanks for answering but onPause() is also called when you start a new activity so surely by overriding that I would be returning the user to the main activity, each time they started a new activity? – NobodyTellsMe Aug 01 '17 at 18:15
  • well, you are overriding it in the activities that you don't want the user to stay in, right? so if you override is in Privacy.java... as soon as I click on the home button, the app will automatically send me back to MainActivity.java. Since I am inside of MainActivity, when I load the app again, it'll go there. Why? Because I am not overriding onPause there... only in Privacy class. – letsCode Aug 01 '17 at 18:18
  • Sorry, I'm struggling slightly to make sense of your comment. Okay, so say said user is in Privacy.java, they can also start a new activity from that one which will take them deeper into the app. I.e, Privacy.java is not the deepest point of the app if you get what I mean. Does my previous point still stand? – NobodyTellsMe Aug 01 '17 at 18:23
  • Ill post code in a second...thatll help you @BoshJailey – letsCode Aug 01 '17 at 18:24
  • Okay, thanks for persevering, stick with it. What if there is a button in Private.java that starts an intent that takes the user to, for example, SuperPrivate.java. Will the code you have put not just return the user to MainActivity.java regardless? :) – NobodyTellsMe Aug 01 '17 at 18:30
  • @BoshJailey YES, it'll still work. You are overriding the onPause method in the actiity the user is in. For example, if you are 2500 screens into the application (which would be a terrible app) and then finally on 2501, you want to take them to 1500, you just code the onPause on THAT activity (the 2501 one) – letsCode Aug 01 '17 at 18:30
  • @BoshJailey I updated my answer with a add-on. Take a look – letsCode Aug 01 '17 at 18:39
0

I don't think this is actually what you want. If they have a lock screen, they had to enter a password on it. You can be fairly sure its the right person. If you want this kind of behavior, you want to timeout the session due to inactivity instead.

Either way, the solution is the same. In onResume, you should check to see if they timed out/went to the lock screen. If so, launch the login screen as a new task (that will prevent the back button from taking you back to this screen) and finish() the current activity. Pass the login screen some info so it can recreate the current activity. When the user is verified, check for that info and create the proper activity from it. If the info doesn't exist, launch your standard homescreen.

The easiest way to pass it enough info to create the new Activity is to pass it a Bundle with the activity name or url, and the results of calling onSaveInstanceState.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
0

Some suggestions:

1) Set up a broadcast receiver that detects a screen on/off intent.

Intent.ACTION_SCREEN_OFF and ACTION_SCREEN_ON

2) Implement broadcast receiver that starts Activity when it matches Screen off.

3) Detect Screen off and on via PowerManager

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();

4) Since screen off happens on onStop() callbacks, therefore, you have to put above code inside onStop() callbacks of your Activity lifecycle. Something like below.

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    boolean isScreenOn = pm.isScreenOff();
if(isScreenOn) {
 sendBroadcast(<your_screenOff_intent);
}

This is the way, but because of the security lock (to open your device), I think, it can't be implemented.

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64