Implementing PIN code checking in an Android application
In one of the mobile applications targeting both iOS and Android that I was working on, there was a new requirement of implementing a PIN code dialog which would ask user to enter a pre-defined code before he can use the application. The dialog would show whenever the application is started, or when it is resumed from the background.
The challenges
On iOS, implementing this would be straight forward by showing a UIAlertView in the didFinishLaunchingWithOptions method, which is called when the application starts, or in the applicationWillEnterForeground method, called when the application is resumed from background. However, on Android, after some experiments, even with the simplest implementation of the PIN code dialog using AlertDialog, there are a few problems:
- There is no well-documented way on Android to tell when the application becomes inactive or resumes from background as the Android application lifecycle is activity-based.
- Even if we know when the application resumes from background, the AlertDialog constructor requires a context parameter to the current activity, which is effectively the activity currently in focus on which the resulting dialog will be shown. Knowing if an activity is currently visible is not immediately obvious.
Checking if the current activity is visible
For (2), you can use the Activity.IsTaskRoot() method to know if the activity is currently having focus, although there are certain exceptions where the method will return wrong values. Alternatively, you can use the ActivityManager class to get information about current activities:
ArrayList<String> runningactivities = new ArrayList<String>();
ActivityManager activityManager = (ActivityManager)getBaseContext().getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (int i1 = 0; i1 < services.size(); i1++) {
runningactivities.add(0,services.get(i1).topActivity.toString());
}
if(runningactivities.contains("ComponentInfo{com.app/com.app.main.MyActivity}")==true){
Toast.makeText(getBaseContext(),"Activity is in foreground, active",1000).show();
}
This is not recommended as it requires the GET_TASKS permission in the application manifest, is resource-intensive, and should only be used in process-management application, as specified in the documentation.
Knowing when the application resumes from background
Problem (1) is a big challenge – unlike iOS, there is no application delegate class with consolidated application lifecycle methods in Android. Each activity will have its own life cycle methods such as onCreate(), onStart(), onStop(), onPause(), onResume() and onDestroy(). Most sources I came across suggest writing a base activity with several variables associating the current activity with its state (e.g. visible, invisible or stopped) each time a life cycle method is called. This requires each activity to inherit the base activity and would cause problem with activity that must inherit something else, e.g. MapFragment or ListActivity (unless you want to use multiple inheritance, which is messy).
Also depending on whether the activity was started using startActivity() or startActivityForResult(), onResume may be called if the activity is brought to foreground programmatically the second time in the application life cycle (as the previous instance is not destroyed yet). Similarly, onStop() may not be always called when the activity is out of view, resulting in confusion in handling the various activity lifecycle methods.
I came across this post which suggests using Application.ActivityLifecycleCallbacks with an approach similiar to iOS application delegate. However, since this callback is available for Android 4.0 and above, it could not be used in my application, which has to support Android 2.2.
My proposed solution
As my application only contains 5 activities, a simple solution is proposed below:
- Create a static variable, nameOfLastStoppedActivity, to store the name of the activity which was last stopped, e.g.goes to background, and defaults to an empty string
- Add a boolean isPINCodeActive inside a static class, e.g. UiUtil.java, to indicate if the PIN code dialog is currently being shown
- Write the public static void promptPINCode(final Context context) method in UiUtil.java to display an AlertDialog from a given context asking user for the PIN code. This method should set and reset the isPINCodeActive as appropriate.
- nameOfLastStoppedActivity would be set in either the onPause() or onStop() method, which will be called when the application goes to the background (and the activity is no longer visible) or if the activity is paused for the next activity to open.
- In the onResume method of every activity where we want to show the PIN code prompt, we will check to compare nameOfLastStoppedActivity with the current activity name. If it is the same activity which was stopped, it is safe to assume that the application was resumed from background (as the user would return to the same activity screen if this is the case) and call promptPINCode to show the PIN code if it’s not already shown. Or if nameOfLastStoppedActivity is empty, the application is being started for the first time and the PIN code dialog will need to be shown. If it is a different activity and onResume was called, we assume it was called due to activity switching and do not show the PIN code prompt.
The code snippet for the above idea is presented below. Notice that this code must be present in every activity where PIN code checking is required:
protected void onResume() {
super.onResume();
if (UiUtil.nameOfLastStoppedActivity.equals(this.getClass().getName()) || UiUtil.nameOfLastStoppedActivity.equals(""))
{
UiUtil.writeToLog("App going from background.");
if (!UiUtil.isPINCodeActive) {
UiUtil.promptPINCode(this);
}
}
}
protected void onPause() {
super.onPause();
UiUtil.nameOfLastStoppedActivity = this.getClass().getName();
}
public void onStop () {
super.onStop();
UiUtil.nameOfLastStoppedActivity = this.getClass().getName();
}
The method this.getClass().getName() is used to retrieve the name of the current activity class, avoiding the need to hard code the activity name.
With the above code snippet, I was able to implement PIN code checking satisfactorily in my application. Although I have to admit that this approach is far from perfect as the same code snippet must be present in different activity classes, it works well in my case.
I hope this article will assist others with similar problems. Feel free to leave a comment if you know of any better approach.
Hi, does this snippet prompt for the phones pin code? What I mean is that when one starts up the phone you get prompted for a pin code before the phone completely starts up… what I want my app to do is when you click a button to turn off a function for it to prompt for the phones pin code, once entered by the user i want to compare the entered pin with the phones pin, if it matches then turn the function off otherwise the function will keep on running.
Hi Christian,
No, this code snippet does not automatically show the phone default prompt for PIN code. All it (and the article) does is to tell you where in an Android application you should strategically place the PIN-code (or other access code) check, by means of a modal AlertDialog prompt for example, before the user can start to use the application, as there may be multiple activities and multiple entry points in an Android application where the user may return to the application from other apps or activities.
Also to answer your question, I don't think there is a way to retrieve the phone's default PIN code or show the default PIN code prompt either, due to security reasons – you would have to make your own, e.g. using an Alert Dialog. Also the user may not have set a PIN code for the phone at all, or he may have use other means to restrict acess to the phone (using a pattern, for example).
Let me know if you have any other questions.