Friday, May 31, 2013

Android: Solution to detect when an Android app goes to the background and come back to the foreground without getRunningTasks or getRunningAppProcesses



In Android, we don’t have options directly to find whether our app goes to background or not like applicationdidenterbackground in iOS.
I have gone through lot of articles and forums; everywhere I am finding only one result.

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

But we can’t use getRunningTasks because android specifies “method is only intended for debugging and presenting task management user interfaces“.

Solution to detect when an Android app goes to the background and come back to the foreground without getRunningTasks or getRunningAppProcesses by using onWindowFocusChanged and onStop method

The solution i tired for my application as shown below.

BaseActivity is a superclass of all the activities.

BaseActivity.java

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;

/**
 * @author Harsha
 *
 *         BaseActivity class extends Activity
 */
public abstract class BaseActivity extends Activity {

protected static final String TAG = BaseActivity.class.getName();

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

public static boolean isAppWentToBg = false;

public static boolean isWindowFocused = false;

public static boolean isMenuOpened = false;

public static boolean isBackPressed = false;

@Override
protected void onStart() {
Log.d(TAG, "onStart isAppWentToBg " + isAppWentToBg);

applicationWillEnterForeground();

super.onStart();
}

private void applicationWillEnterForeground() {
if (isAppWentToBg) {
isAppWentToBg = false;
Toast.makeText(getApplicationContext(), "App is in foreground",
Toast.LENGTH_SHORT).show();
}
}

@Override
protected void onStop() {
super.onStop();

Log.d(TAG, "onStop ");
applicationdidenterbackground();
}

public void applicationdidenterbackground() {
if (!isWindowFocused) {
isAppWentToBg = true;
Toast.makeText(getApplicationContext(),
"App is Going to Background", Toast.LENGTH_SHORT).show();
}
}

@Override
public void onBackPressed() {

if (this instanceof MainActivity) {

} else {
isBackPressed = true;
}

Log.d(TAG,
"onBackPressed " + isBackPressed + ""
+ this.getLocalClassName());
super.onBackPressed();
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {

isWindowFocused = hasFocus;

if (isBackPressed && !hasFocus) {
isBackPressed = false;
isWindowFocused = true;
}

super.onWindowFocusChanged(hasFocus);
}

public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
case R.id.action_settings:
Intent i = new Intent(this, SettingActivity.class);
startActivity(i);
break;
}
return true;
}

@Override
public boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
case R.id.action_settings:
Intent i = new Intent(this, SettingActivity.class);
startActivity(i);
break;
}
return true;
}

}

Each of the activity classes extendes BaseActivity class to track the application status.

MainActivity.java

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;

public class MainActivity extends BaseActivity {

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

((ImageButton) findViewById(R.id.SecondBtn))
.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),
SecondActvity.class));
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

SecondActvity.java

import android.os.Bundle;

public class SecondActvity extends BaseActivity {

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

}
 App output looks as :




Improvements
If you want to see an example without having common base class, see app-foreground-n-background-using-callbacks-in-application. It also gives the Solution to detect when an Android app goes to the background and come back to the foreground without getRunningTasks or getRunningAppProcesses. This example supports from API level 14 and above because they interface classes used are added from API level 14.

Source Code
You can download the source code by clicking here: AppStatus-SourceCode.  This project is built using eclipse IDE. Unzip and import the project into Eclipse, it’s a good idea to use the Project by clean and rebuild from the project menu. It works in all API levels.


Thanks for reading :) 
Whether this post is helpful?
If you have any other quick thoughts/hints that you think people will find useful, feel free to leave a comment.