How to force killing application when minimizing in Android - android

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.

Related

How to stop/pause an Android Service when all activities are hidden

I'm working on designing an Android app, and I'm looking for information on how to implement what I thought would be a common pattern, but I can't seem to find any information about it online.
I'm going to have an application with multiple activities, and all of these activities are going to be communicating with a WiFi device over a network. So my first thought is that, since all Activities will need access to this central 'network communicator', I should create a Service to hold all of the network communication code. Great!
But now, I want to be able to save battery life, and in general make the application less obtrusive. If none of the activities from my app are visible, then I don't need to be running the network code, and I can shut down my sockets and wait. Then, when one of the activities becomes visible again, I can reconnect at that point.
Here's the problem - I don't know how to do this! Is there an existing method or pattern used to determine when all activities from my app are hidden? I don't want to disconnect/reconnect every time one of my Activities resumes or is created, because most of the time that will be from the user navigating around the app, switching between activities. I want the network connection open this whole time.
I could put some sort of 'reference counting' mechanism in my Service to keep track of when Activities appear and disappear, but that seems like overkill. I'm surprised there isn't an existing mechanism for this.
Sorry if I'm missing something simple...
You can check it using:
public void onPause(){
super.onPause();
Context context = getApplicationContext();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
if (!taskInfo.isEmpty()) {
ComponentName topActivity = taskInfo.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
StopPlayer();
Toast.makeText(xYourClassNamex.this, "YOU LEFT YOUR APP. SERVICE STOP", Toast.LENGTH_SHORT).show();
}
}
}
Put the above code in on Pause() of every Class. It will toast when your app will go on recent application by clicking on home or back button. Now you can set any preference instead of this Toast & stop the services. Now check your preference on Resume() of every Class. If preference has any value then u can start your service.
The pattern is to bind the Service to a base class Activity, and then to inherit your multiple classes from that one bound Activity.
A Bound Service sounds like exactly what you need: http://developer.android.com/guide/components/bound-services.html#Binder
Since you have multiple activities using this same service, you can Bind each one to the service, and Unbind the activity when it no longer needs to use the service. A great place to do this would be in your onStart() and onStop() Activity lifecycle methods. The great thing about this is that when the Service detects that there is no Activity bound to it, it will kill itself.
Further reading: Great explanation by user hackbod on why onStart()/onStop() is a good option: https://stackoverflow.com/a/2304794/1839500

Activity losing focus due to another Activity in the same app vs a separate app / device interrupt

I need to log an event only when the application as a whole has lost focus (such as an incoming call or the home key).
What is the best way to tell an Activity has been paused or stopped from an external event rather than another activity within the same app?
Thanks
when your activity stops you can check if your app is still on top in the onStop() method:
private boolean isAppOnTop(){
ActivityManager am = (ActivityManager) getSystemService(Service.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks;
tasks = am.getRunningTasks(1);
RunningTaskInfo running = tasks.get(0);
if(running.topActivity.getPackageName().equals("your.package.name")){
return true;
}
else{
return false;
}
}
This method queries all running tasks first. In general the task which has focus is on top of the stack. Check if this task belongs to your package.
So along with Dennis' answer which is perfectly valid if you are willing to set the get_tasks permission we explored the following options (mostly based on setting some kind of flag when onstop/onpause is called):
When starting a new activity. In the onStop method if the flag is set to true then the interrupt was caused by a new activity in the app.
Have a global variable which counts active views. Increments on onStarts and decrements on onStops. If its 0 then we have no visible activities.
onStart sets an active flag true for the activity. onStop sets the flag to false. (states can also be saved to prefs). When the activity loses focuses we can check all the flags.
Because the app is relatively simple we went for the first option.

Implement Passcode Lock for Android Application

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.

Application-level onPause

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.

Determining if app is running in background

How can I tell that my application has been put in the background (i.e. none of my app's activities are visible anymore)? I need to determine this so that I can release resources shared among several of my activities (graphics, sound clips and a socket connection).
I've thought about keeping a global counter that's incremented in the activities' onStart() method, and decremented in onStop(). If the counter reaches zero, then all activities have been stopped and my app is running in the background. However I'm not sure if this is going to be 100% reliable. Also, I can't help but think that there must be a better way of doing this.
You shouldn't need to know this, but to answer you:
in your main activity:
public boolean inBackground;
#Override
public void onPause()
{
inBackground=true;
super.onPause();
}
#Override
public void onResume()
{
inBackground=false;
super.onResume();
}
Aren't you after the OnPause()/OnResume() events?
http://developer.android.com/reference/android/app/Activity.html#onPause()
http://developer.android.com/reference/android/app/Activity.html#onResume()
See http://developer.android.com/guide/topics/fundamentals.html#lcycles for an overview.
You could use a global counter assuming it is kept in persistent storage. Always keep in mind the system is free to unload and reload activities from device RAM based on pressure from other apps so instance variables of activities are probably not a good choice to house that data.
I think the Android way of handling a scenario like yours would be to manage your connection state in a service and use persistent storage to monitor application state.
If you need this functionality maybe your architecture is not well designed. Each activity must be in some way "standalone" so when it's stopped release any data associate with it. If you need to store some persistant data between activities use sql or some other data storage and if you need some shared resources between activities put them in service. Try to isolate any coupling between activities.
I haven't tried this myself yet, but I think the following would work.
Create your own custom Activity class that extends Activity (as suggested in this SO question).
Ensure all your activites extend your custom Activity class.
#Override the onStop() method (as per the Activity life cycle docs).
Have onStop() call the utility method below (code based on Droid-Fu project) to figure out if your app is now in the background.
My worry is there could be some timing windows when your last activity closes before its new activity (also i your app) launches, but hopefully that is avoidable.
public static boolean isApplicationInBackground(Context context)
{
ActivityManager am =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty())
{
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName()))
{
return true;
}
}
return false;
}
Let us know how you get on as I might need to implement this feature too!
Update: I've done some quick testing of this code.
It seems to work fine when pressing the home key to exit the application, as the running task changes to ComponentInfo{com.motorola.blur.home/com.motorola.blur.home.HomeActivity} on my device. However, the code doesn't detect the app as in the background when using the back button to exit the application (it still thinks my app is running).
This scenario can be detected by overriding onDestroy() in your top level activities, and checking isFinished() to confirm that the app is getting destroyed (rather than the activity being recreated, e.g. for an orientation change).

Categories

Resources