My application has sensitive user information and we need to implement a passcode screen to be displayed whenever the user opens the application.
Here are the two approaches I tried after reading this post.
Use a static variable and reset it in onStop() of each activity and check it again in the onStart() of each activity and show the passcode screen if the time crossed a minimum threshhold say 1-2 secs. The problem with this approach is that my application also uses intents to call camera and barcode scanners and the users may spend longer periods in these external apps. I can increase the threshold in this case but it makes the calculations complicated and is not a very good solution.
I tried the other approach by using this method.
protected boolean isAppOnForeground(final Context context) {
List<RunningAppProcessInfo> appProcesses = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (RunningAppProcessInfo appProcess : appProcesses) {
if ((appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) &&
appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
But this will always return true when I check for it in the onStart method of each activity since the process already started by the time it is in onStart
Is there any other approach that I can take to display a passcode when ever the user opens the application? It should be displayed even when user clicks on home screen to go out of the app and then comes back to the app from recent apps.
i've implemented this exact feature. i essentially did your #1, but in a little cleaner way.
what i did was write an abstract subclass of Activity, and override onResume(). in there, decide if you need to show the pin lock screen. if you do, finish yourself and start the pin lock activity. have all your activities extend this activity.
to remember where you were at, you can add a "starting intent" extra to the intent used to start the pin lock activity. when the app is unlocked, the pin lock activity can use that extra to put the user right back where they were.
if your app was fragment-based, this would be simple. whenever the activity that hosts all of the fragments is resumed, you show the pin lock fragment. that's all.
the problem with an app consisting of a bunch of activities is that there is no clear defining moment of "starting" the app. the concept doesn't exist. this is essentially the problem you found with your #1 solution. onResume() seems like a good choice but that can be called for lots of reasons. for example, the user start activity A, which starts activity B. now they press back. show the pin lock, or not?
any solution that utilizes a thread that checks foreground processes is a terrible idea because of the battery impact.
finally, you might want to question the requirement of having a pin lock every time the app is brought into the foreground. it seems excessive if i bounce out to read a text message and come back 10s later i'm forced to re-enter a pin. time based seems more appropriate.
Related
I have an app that I use that automatically locks (requires a password entry screen)...
a) when the app starts for the first time
b) when another app is started and takes over the device
c) when the device "sleeps" automatically
d) when the user "sleeps" the device manually
(note: with c & d, if the user "awakens" the app within 5 seconds, it does NOT ask for the password)
I would like to do this for an app that I am writing as well so I created an activity (PasswordActivity) with the necessary verification steps and have it working properly.
I originally placed it in the ONCREATE of the MainActivity (that was LAUNCHED in the AndroidManifest). That seemed to work fine. But then started on the re-lock capability. So tried moving it to the ONSTART or ONRESUME. But then when another ACTIVITY in my APP took focus, or the screen rotated, then the PasswordActivity fired again. That won't work.
On here I found a thread recommending making it the LAUNCHER app in the Manifest and then when the password is OK, call the MainActivity. That seemed to work better... but then ran into an issue that it "re-locks" only when the user pressed the back button off the main screen (I assume stopping my app) and starts the app again. Doesn't catch another APP taking over the device although.
So based on that, and after looking at all documentation I can find on lifecycles (although most of those are ACTIVITY based, not APPLICATION based) not sure how to catch when the APP itself loses focus (another app takes over) and how to handle the automatic or manual sleep (C & D above) along with the time delay checking. I am assuming it is going to be a combination of several calls or steps, but not sure exactly which ones at this point.
Any recommendations?
I think I figured it out using some of the suggestions as well as some research. I am posting my solution here in case it can help someone else, someone sees a definite flaw I didn't see or encounter yet, or someone has some other input that may improved it.
As mentioned, the biggest problem as I can tell is there isn't any built-in function calls that can determine when "your app" is not in the foreground. The onPause and onResume are activity based, not app based... so changing from activity to activity in your app and even screen rotation, fires them.
To get around this... I created a global variable called gv.appPauseTime. I created two separate universal utility functions I can call from anywhere in my app.
public static void lockAppStoreTime() {
gv.appPauseTime=new Date();
}
public static void lockAppCheck(Activity act) {
boolean bLock=false;
// Check to see if this is the first time through app
if (gv.appPauseTime==null) {
bLock = true;
} else {
Date currTime = new Date();
long diffMillis = currTime.getTime() - gv.appPauseTime.getTime();
long diffInSec = TimeUnit.MILLISECONDS.toSeconds(diffMillis);
// Lock app if 120 seconds (2 minutes) has lapsed
if (diffInSec > 120) {
bLock=true;
}
}
gv.appLastPause = new Date();
if (bLock) {
Intent j = new Intent(act, PasswordActivity.class);
act.startActivity(j);
}
}
In every activity, I create (or modified) the onPause and onResume like so...
#Override
public void onPause(){
super.onPause();
Util.lockAppStoreTime();
}
#Override
public void onResume(){
super.onResume();
Util.lockAppCheck(this);
}
When the onPause fires, it stores the current date (time). When an onResume fires in any function, it compares the current date (time) to the stored one. If 120 seconds (2 minutes) has lapsed, it displays the PasswordActivity to get the password verified. It does "store" the current date(time) before it calls the PasswordActivity so that it doesn't keep re-iterating it.
Basically, if there is a 2 minute gap, between an activity pausing and any activity, IN MY APP, resuming (or starting), it prompts for the password. If the user opens a different APP, and then returns to my APP... again, if it is at least a 2 minute lapse, then the password is asked for. If the user powers off or closes the app, then the restart will also trigger the password.
I chose 2 minutes, because that seems to be a decent "time lapse" in our app in general. Of course, that can be changed too.
So far, so good. Hope this helps anyone else.
Right now I'm developing some kind of password manager & generator application and it is almost done but I'm facing a problem that seems a bit hard to solve.
In order yo improve the security of my app, I added a timer so if the user is under inactivity the app will close. Now I'd like to close the app every time the user want to minimize it (pressing the "HOME" button and the "RECENT APPS" button).
I've tried onStop() and onPause() but nothing works because every activity on my app when is replaced by another moves through the mentioned states. Since I can't use Keycode == Keycode.HOME how could I do that?
I suggest that all your activities keep track of their state in onResume() and onPause(): they can increment some static application-wide variable counting how many of your activities are visible.
When this count goes to zero, it means none of your activities are visible and you can do your cleanup.
The solution proposed by #Don Chakkappan is not good enough I think, as your activity can be reported as active even though it's no longer visible.
Continuously check the Activity in the for ground from a service
ActivityManager am = (ActivityManager) this .getSystemService(ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
#SuppressWarnings("unused")
ComponentName componentInfo = taskInfo.get(0).topActivity;
Log.d(TRCU.LOG_TAG, "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
String currentActivity=taskInfo.get(0).topActivity.getClassName();
Then you can finish() or stop Service according to the context
The Android usage model tells you not to do that. You can disable all password generating/managing when going to background and re-enable it when resuming. This will also be much faster performance-wise than killing the application on pause.
Since your app is a security enabling application, all sensitive information should be inaccessible at onPause anyway, so this shouldn't impact the design too much.
My application is launched using a tag, and based on the information contained in tag, it further proceeds. Now my app can also be started by using touching icon, and later it asks user to touch the tag. Small flow would be as below.
So MainActivity may contains tag data(if started from TagProcessorActivity), or may not contain data (if started from icon launch). Data is passed as intent extra value from TagProcessorActivity to IconLaunchActivity then to MainActivity. After main activity, app operation proceeds. When I leave the main activity, all my previous activities gets finish. I have checked onDestroy() is called for each activity. Now if I logout after MainActivity, (Logout simply a feature that closes all existing activity), and relaunch my application from recent app, my tag details still appears in MainActivity, which I dont know why.
To make is more clear my questions are:
1) Why activity which was destroyed still contains the information from previous launch.
2) I know about removeExtra() method, but is there some better options to tackle this problem.
3) and none the less, is there some thing wrong in my code or android is keeping that instance of intent extra?
PS: Not clear which piece of code to post, so if required feel free to ask for code.
Applications never exit in Android. onDestroy only destroys the activity, not any static variables left in the app. These will keep their value the next time an Activity is launched. This can be combined with some other features (like launching from the recent tasks menu causing you to launch the same intent) and this is the behavior you will get. The answer I always used was to detect this case (by checking the intent, there's a field that says if this is a restart or fresh), and ignoring the intent extras if so.
A finished task launched from Recents (as opposed to home screen launcher icon) will receive the old Intent, including its Extras, Data, etc. There is a way to know that it was launched from Recents so you can handle the Intent appropriately.
protected boolean wasLaunchedFromRecents() {
return (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY;
}
In my humble opinion, that flag is poorly named (other flags referencing the Recents list actually use that word, e.g. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, FLAG_ACTIVITY_RETAIN_IN_RECENTS) and the documentation was never updated to reflect the fact that many popular Android devices have a dedicated button for Recents:
This flag is not normally set by application code, but set for you by the system if this activity is being launched from history (longpress home key).
I have an Activity that should only get created once. That is, onCreate can only be called once. If it's called again, I want the Activity to do nothing.
Is it advisable to do the following?
protected void onCreate(Bundle savedInstanceState) {
this.setTheme(android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
super.onCreate(savedInstanceState);
if(onCreateWasCalledAlreadyBoolean) {
setResult(RESULT_OK);
finish();
return;
}
//Do other stuff here
}
I assume you understand how the activity life cycle works. I mean, you are not trying to avoid something that does not apply here (thinking that onCreate may be called multiple times whenever it just onRestarts etc.).
Technically, it's perfectly fine.
However, you should be worrying more about why you need to call your activity ("A") again if it shouldn't be created at all, if that's what you're thinking.
If you've caught yourself checking if your activity A was already "called" (?), this could mean the previous activity ("B") has a mistake in the logic flow of the app, and that B instead should be checking if it must in fact start that activity A. I mean, if you need to decide if you must call an activity, check before starting it.
I don't think that's applicable if you're restarting the activity (e.g.: go Home, then navigate back), but then again you should be restarting it from where you left (B for what I can tell). You won't be navigating back to A. And you didn't give much detail, so I'd guess this is some kind of splash screen, like evilmage93 said.
If that's indeed some kind of splash screen, I would advise to show it whenever the user navigates back all the way to remove your app from the task stack (contrary to his advice). In other words, whenever the user restarts the app from its "front door".
Although that's ultimately a design decision, I prefer to see the splash screen whenever the app is being loaded ("entered") in the stack for the first time, and it should work fine if you (obviously) finish A before calling B (the splash screen is supposed to finish itself when done, even in its first run). It's a matter of consistency: the same app should behave the same way whenever the user performs the same task (start app from its "front door").
Still, I answered your question covering some general aspects because you asked in such way.
// edited:
Finally, by looking at that onCreateWasCalledAlreadyBoolean I'm afraid you may be trying to reinvent part of the activity life cycle mechanism. In this case, don't: proceed with your regular activity logic because the user expects that behavior. Generally I wouldn't advise people to break the normal loading of an activity just because it was killed and restarted by the system.
I don't see why not. Wouldn't it be simpler to not restart the activity at all though?
What are you worried about NOT being okay? Performance..Uncaught exceptions..Code clarity?
I know that Android doesn't have an Application-level onPause the way an Activity has an onPause, but it looks like I need a similar functionality. I asked a (dumb) question awhile ago (http://stackoverflow.com/questions/9508301/checking-the-manner-in-which-an-activity-has-been-reanimated) and realized that onPause/onResume will give me what I'm looking for: The ability to know if the Activity has been relegated to the background (either via the Home button, opening a different app, the phone sleeping, etc.).
The trouble is, onPause also fires in an Activity as I go from one Activity in my App to another. So I'm heading down the road of finding EVERY SINGLE way a user can go from Activity to Activity in my app (and there are anywhere from 15 to 25 of those) and setting a flag right before each user-initiated Activity switch. Then in onResume I'm checking that flag and if it's X, I know that it was a user-initiated onResume. If the flag is Y, I know that it's a result of the Home button being pressed, the phone sleeping, etc.).
This just seems like a big messy kludge. Is there a better way?
(I add this to help, not to confuse: My goal is to have a (very annoying, I know) passcode screen that pops up any time the app goes away from the foreground for any reason (even screen sleep). I planned to use onPause/onResume to check when to pop the Passcode activity, but onPause fires every time I change Activities and therefore I need to add more.)
Apps like Pinnacle Locker do something similar--I believe they intercept an Intent for specific packages, and thus are fired whenever the system switches to an app, but not when the user changes screens between apps.
I may be misunderstanding you, but another approach would be to turn your approach around: whenever you change from one screen to the other inside an app, you are either launching an Intent or finish()ing (such as when the user presses the back button), right?
Then you could set a flag in your internal Intents, and whenever you directly call finish() or when onBackPressed() is called in your apps you could set a similar flag. If that flag is not set when onResume() runs in your app, then you could show the lockscreen.
This doesn't really answer your question, but perhaps as an alternate approach, could you make an activity base class that does the flag management and have all your activities inherit from that? You could even do something like set a timeout of, say, 5 seconds between one activity's onPause and another activity's onResume to decide that you were doing an out-of-app switch instead of an in-app switch, although if you just have a utility function that you call from your activity base class for initiating an activity change, that still vastly reduces the surface area of where you have to keep track of things.
You should combine the onPause() function with application visibility status and process further if your application is not visible. Following code might help:
private boolean isVisible(String app) {
ActivityManager activityManager = (ActivityManager) getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
Log.i("Foreground App", appProcess.processName);
if (app.equalsIgnoreCase(appProcess.processName)) {
return true;
}
}
}
return false;
}
Again this might not fit in your current scenario entirely.