116

I wish to have one application that runs in the background, which knows when any of the built-in applications (messaging, contacts, etc) is running.

So my questions are:

  1. How I should run my application in the background.

  2. How my background application can know what the application currently running in the foreground is.

Responses from folks with experience would be greatly appreciated.

Swati Garg
  • 995
  • 1
  • 10
  • 21
dhaiwat
  • 1,731
  • 4
  • 16
  • 16
  • I don't think you've given an adequate explanation of what you're trying to do. *What* is your background application trying to do? In what ways should it be able to interact with the user? *Why* do you need to know what the current foreground app is? Etc. – Charles Duffy Jan 30 '10 at 05:30
  • http://codingaffairs.blogspot.com/2016/05/check-if-your-android-app-is-in.html – Developine May 25 '16 at 03:39
  • 1
    for detecting the foreground app, you can use https://github.com/ricvalerio/foregroundappchecker – rvalerio Aug 28 '16 at 22:57

14 Answers14

104

With regards to "2. How my background application can know what the application currently running in the foreground is."

Do NOT use the getRunningAppProcesses() method as this returns all sorts of system rubbish from my experience and you'll get multiple results which have RunningAppProcessInfo.IMPORTANCE_FOREGROUND. Use getRunningTasks() instead

This is the code I use in my service to identify the current foreground application, its really easy:

ActivityManager am = (ActivityManager) AppService.this.getSystemService(ACTIVITY_SERVICE);
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);

Thats it, then you can easily access details of the foreground app/activity:

String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
PackageManager pm = AppService.this.getPackageManager();
PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0);
String foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString();

This requires an additional permission in activity menifest and works perfectly.

<uses-permission android:name="android.permission.GET_TASKS" />
arslan haktic
  • 4,348
  • 4
  • 22
  • 35
Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90
  • 1
    This should be marked as the correct answer. Required permission: android.permission.GET_TASKS is the only very minor downside to other methods that can avoid it. – brandall Mar 04 '13 at 19:24
  • 3
    EDIT TO THE ABOVE: Note: this method is only intended for debugging and presenting task management user interfaces. <-- Just spotted that in the Java doc. So this method might change without notice in the future. – brandall Mar 04 '13 at 19:31
  • 53
    Note: `getRunningTasks()` is deprecated in API 21 (Lollipop) - http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks(int) – dtyler Nov 02 '14 at 21:56
  • @dtyler, OK then. Is there any other method instead? Although Google does not want third-party apps obtain sensitive information, I have the device's platform key. Is there any other method do the same if I do have system privilege? – Yeung Dec 16 '14 at 03:11
  • 2
    There is a way use "Usage statistics api" introduced in 21 – KunalK May 26 '15 at 13:24
  • `The first in the list of RunningTasks is always the foreground task.` it is false in marshmellow – zihadrizkyef Sep 19 '17 at 13:27
  • I made this utility class that solves this issue without any special permissions or API level restrictions https://gist.github.com/pantos27/6faad313ba364c947e2487e80aa25c86 – pantos27 Jul 19 '18 at 20:14
38

Try the following code:

ActivityManager activityManager = (ActivityManager) newContext.getSystemService( Context.ACTIVITY_SERVICE );
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses){
    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
        Log.i("Foreground App", appProcess.processName);
    }
}

Process name is the package name of the app running in foreground. Compare it to the package name of your application. If it is the same then your application is running on foreground.

I hope this answers your question.

Friedrich Bhaer
  • 389
  • 3
  • 2
38

i had to figure out the right solution the hard way. the below code is part of cyanogenmod7 (the tablet tweaks) and is tested on android 2.3.3 / gingerbread.

methods:

  • getForegroundApp - returns the foreground application.
  • getActivityForApp - returns the activity of the found app.
  • isStillActive - determines if a earlier found app is still the active app.
  • isRunningService - a helper function for getForegroundApp

this hopefully answers this issue in all extend (:

private RunningAppProcessInfo getForegroundApp() {
    RunningAppProcessInfo result=null, info=null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningAppProcessInfo> l = mActivityManager.getRunningAppProcesses();
    Iterator <RunningAppProcessInfo> i = l.iterator();
    while(i.hasNext()){
        info = i.next();
        if(info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                && !isRunningService(info.processName)){
            result=info;
            break;
        }
    }
    return result;
}

private ComponentName getActivityForApp(RunningAppProcessInfo target){
    ComponentName result=null;
    ActivityManager.RunningTaskInfo info;

    if(target==null)
        return null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <ActivityManager.RunningTaskInfo> l = mActivityManager.getRunningTasks(9999);
    Iterator <ActivityManager.RunningTaskInfo> i = l.iterator();

    while(i.hasNext()){
        info=i.next();
        if(info.baseActivity.getPackageName().equals(target.processName)){
            result=info.topActivity;
            break;
        }
    }

    return result;
}

private boolean isStillActive(RunningAppProcessInfo process, ComponentName activity)
{
    // activity can be null in cases, where one app starts another. for example, astro
    // starting rock player when a move file was clicked. we dont have an activity then,
    // but the package exits as soon as back is hit. so we can ignore the activity
    // in this case
    if(process==null)
        return false;

    RunningAppProcessInfo currentFg=getForegroundApp();
    ComponentName currentActivity=getActivityForApp(currentFg);

    if(currentFg!=null && currentFg.processName.equals(process.processName) &&
            (activity==null || currentActivity.compareTo(activity)==0))
        return true;

    Slog.i(TAG, "isStillActive returns false - CallerProcess: " + process.processName + " CurrentProcess: "
            + (currentFg==null ? "null" : currentFg.processName) + " CallerActivity:" + (activity==null ? "null" : activity.toString())
            + " CurrentActivity: " + (currentActivity==null ? "null" : currentActivity.toString()));
    return false;
}

private boolean isRunningService(String processname){
    if(processname==null || processname.isEmpty())
        return false;

    RunningServiceInfo service;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningServiceInfo> l = mActivityManager.getRunningServices(9999);
    Iterator <RunningServiceInfo> i = l.iterator();
    while(i.hasNext()){
        service = i.next();
        if(service.process.equals(processname))
            return true;
    }

    return false;
}
Sven Dawitz
  • 381
  • 3
  • 4
  • 1
    if you don't mind me asking, why do you need the methods to see if it is running a service, is still active, and to get activity just to get the foreground application? –  Jun 13 '11 at 19:49
  • 2
    sorry, but it isn't working. Some apps are filtered without a reason, I think it's when they run some service. So isRunningService is kicking them out. – seb Aug 18 '12 at 10:46
  • 16
    Unfortunately, getRunningTasks() has been deprecated since Android L(API 20). As of L, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive. – Sam Lu Jun 29 '14 at 06:11
  • Does anyone know how this approach is better than simply doing `mActivityManager.getRunningTasks(1).get(0).topActivity`? – Sam Feb 17 '19 at 10:00
29

From lollipop onwards this got changed. Please find below code, before that user has to go Settings -> Security -> (Scroll down to last) Apps with usage access -> Give the permissions to our app

private void printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e(TAG, "Current App in foreground is: " + currentApp);
}
Suman
  • 4,221
  • 7
  • 44
  • 64
  • 1
    cant this usage stats be set programmatically? – rubmz Jul 26 '15 at 12:12
  • @rubmz Have you got any solution on this? – VVB Jan 23 '16 at 18:12
  • 1
    in my doogee with android 5.1 there is no option "Give the permissions to our app" – djdance Oct 08 '16 at 14:40
  • This doesn't produce 100% accurate results... it's right most of the time – CamHart Sep 17 '18 at 19:45
  • @CamHart, can you remember any specifics about when it produced inaccurate results? Was it the `queryUsageStats()` path or the `getRunningAppProcesses()` path that has the issue? – Sam Feb 17 '19 at 10:03
  • 1
    This won't show the last App in foreground, but maybe the last "used" in a way - if you leave an activity and go to another one, pull the launcher menu from above, and release it, you will be in the "wrong" activity until you go to another one. It doesn't matter if you scroll the screen or not, it reports the wrong activity until you launch another one. – Azurlake Apr 08 '19 at 19:03
10

In order to determine the foreground application, you can use for detecting the foreground app, you can use https://github.com/ricvalerio/foregroundappchecker. It uses different methods depending on the android version of the device.

As for the service, the repo also provides the code you need for it. Essentially, let android studio create the service for you, and then onCreate add the snippet that uses the appChecker. You will need to request permission however.

rvalerio
  • 489
  • 5
  • 5
  • perfect!! tested on android 7 – Pratik Tank Dec 29 '18 at 10:48
  • Thanks so much. This is the only answer that solved the issue we were facing with notifications of apps. When a notification pops up, all other answers will give back the package name of the app that sent the notification. Your LollipopDetector solved this issue. One hint: from API 29, MOVE_TO_FOREGROUND is deprecated in UsageEvents.Event, so from Android Q onward one should use ACTIVITY_RESUMED. Then it will work like a charm! – Jorn Rigter May 21 '20 at 11:36
9

For cases when we need to check from our own service/background-thread whether our app is in foreground or not. This is how I implemented it, and it works fine for me:

public class TestApplication extends Application implements Application.ActivityLifecycleCallbacks {

    public static WeakReference<Activity> foregroundActivityRef = null;

    @Override
    public void onActivityStarted(Activity activity) {
        foregroundActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (foregroundActivityRef != null && foregroundActivityRef.get() == activity) {
            foregroundActivityRef = null;
        }
    }

    // IMPLEMENT OTHER CALLBACK METHODS
}

Now to check from other classes, whether app is in foreground or not, simply call:

if(TestApplication.foregroundActivityRef!=null){
    // APP IS IN FOREGROUND!
    // We can also get the activity that is currently visible!
}

Update (as pointed out by SHS):

Do not forget to register for the callbacks in your Application class's onCreate method.

@Override
public void onCreate() {
    ...
    registerActivityLifecycleCallbacks(this);
}
Sarthak Mittal
  • 5,794
  • 2
  • 24
  • 44
  • 2
    That's what I've been looking for since the past two hours. Thank you! :) – waseefakhtar Mar 24 '18 at 18:44
  • 2
    We need to call "registerActivityLifecycleCallbacks(this);" inside Override method "onCreate()" of Application class (TestApplication). This will start the callbacks in our application class. – SHS Jun 03 '19 at 07:26
  • @SarthakMittal, If a device goes into a standby mode or when we lock it, then onActivityStopped() will be called. Based on your code, this will indicate that the application is in background. But it is in foreground actually. – Ayaz Alifov Sep 12 '19 at 14:11
  • @AyazAlifov If phone is on standby or phone is locked, I won't consider it to be in foreground, but again it depends upon preference. – Sarthak Mittal Sep 12 '19 at 14:50
8

Taking into account that getRunningTasks() is deprecated and getRunningAppProcesses() is not reliable, I came to decision to combine 2 approaches mentioned in StackOverflow:

   private boolean isAppInForeground(Context context)
    {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
            String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

            return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
        }
        else
        {
            ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
            ActivityManager.getMyMemoryState(appProcessInfo);
            if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
            {
                return true;
            }

            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            // App is foreground, but screen is locked, so show notification
            return km.inKeyguardRestrictedInputMode();
        }
    }
Ayaz Alifov
  • 8,334
  • 4
  • 61
  • 56
5

The ActivityManager class is the appropriate tool to see which processes are running.

To run in the background, you typically want to use a Service.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
1

This worked for me. But it gives only the main menu name. That is if user has opened Settings --> Bluetooth --> Device Name screen, RunningAppProcessInfo calls it as just Settings. Not able to drill down furthur

ActivityManager activityManager = (ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE );
                PackageManager pm = context.getPackageManager();
                List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
                for(RunningAppProcessInfo appProcess : appProcesses) {              
                    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(appProcess.processName, PackageManager.GET_META_DATA));
                        Log.i("Foreground App", "package: " + appProcess.processName + " App: " + c.toString());
                    }               
                }
  • 5
    I think this works only if your own app is running in the foreground. It doesn't report anything when some other app is on the foreground. – Alice Van Der Land Jan 26 '19 at 20:05
1

An easy solution is to use LiveData. Create a singleton LiveData variable. (probably in a plain Kotlin file).

val foregroundHelper = MutableLiveData<Unit>()

Observe From Activity or Fragment:

foregroundHelper.observe(this, Observer {}) // for Activity
foregroundHelper.observe(viewLifecycleOwner, Observer {}) // for Fragments

Now from Your Background Service, Broadcast Receiver, etc:

val appIsVisibleToTheUser = foregroundHelper.hasActiveObservers()
// Now your logic goes.
if (!appIsVisibleToUser) {
   // App is in background
   // So In my case:
   // I'm showing Notification to the user for the error happened in Background Service.
}

Thanks.

Riajul
  • 1,140
  • 15
  • 20
0

Do something like this:

int showLimit = 20;

/* Get all Tasks available (with limit set). */
ActivityManager mgr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> allTasks = mgr.getRunningTasks(showLimit);
/* Loop through all tasks returned. */
for (ActivityManager.RunningTaskInfo aTask : allTasks) 
{                  
    Log.i("MyApp", "Task: " + aTask.baseActivity.getClassName()); 
    if (aTask.baseActivity.getClassName().equals("com.android.email.activity.MessageList")) 
        running=true;
}
ChristianS
  • 63
  • 2
  • 7
  • Google will probably reject an app that uses ActivityManager.getRunningTasks(). The documentation states that it is foe dev purposes only : http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks%28int%29 See the intial comment : http://stackoverflow.com/questions/4414171/how-to-detect-when-an-android-app-goes-to-the-background-and-come-back-to-the-fo#comment16801122_9876683 – ForceMagic Jan 14 '14 at 21:44
  • 3
    @ForceMagic - Google does not reject apps simply for using *unreliable* APIs; further, Google is not the only distribution channel for Android applications. That said, it is worth keeping in mind that the information from this API is not dependable. – Chris Stratton May 27 '14 at 19:53
  • @ChrisStratton Interesting, but you're probably right, although it is discouraged to use it for core logic they won`t reject the app. You might want to warn Sky Kelsey from the comment link. – ForceMagic May 27 '14 at 20:18
  • 3
    "getRunningTasks" does work for api 21and beyond so it might be good idea to figure another way to make it work for LOLLIPOP ( i myself havent figured this out myself though :( ) – amIT Jan 28 '15 at 11:47
0

In lollipop and up:

Add to mainfest:

<uses-permission android:name="android.permission.GET_TASKS" />

And do something like this:

if( mTaskId < 0 )
{
    List<AppTask> tasks = mActivityManager.getAppTasks(); 
    if( tasks.size() > 0 )
        mTaskId = tasks.get( 0 ).getTaskInfo().id;
}
rubmz
  • 1,947
  • 5
  • 27
  • 49
0

This is how I am checking if my app is in foreground. Note I am using AsyncTask as suggested by official Android documentation.`

`

    private class CheckIfForeground extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... voids) {

        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                Log.i("Foreground App", appProcess.processName);

                if (mContext.getPackageName().equalsIgnoreCase(appProcess.processName)) {
                    Log.i(Constants.TAG, "foreground true:" + appProcess.processName);
                    foreground = true;
                    // close_app();
                }
            }
        }
        Log.d(Constants.TAG, "foreground value:" + foreground);
        if (foreground) {
            foreground = false;
            close_app();
            Log.i(Constants.TAG, "Close App and start Activity:");

        } else {
            //if not foreground
            close_app();
            foreground = false;
            Log.i(Constants.TAG, "Close App");

        }

        return null;
    }
}

and execute AsyncTask like this. new CheckIfForeground().execute();

Developine
  • 12,483
  • 8
  • 38
  • 42
-1

I combined two solutions in one method and it works for me for API 24 and for API 21. Others I didn't test.

The code in Kotlin:

private fun isAppInForeground(context: Context): Boolean {
    val appProcessInfo = ActivityManager.RunningAppProcessInfo()
    ActivityManager.getMyMemoryState(appProcessInfo)
    if (appProcessInfo.importance == IMPORTANCE_FOREGROUND ||
            appProcessInfo.importance == IMPORTANCE_VISIBLE) {
        return true
    } else if (appProcessInfo.importance == IMPORTANCE_TOP_SLEEPING ||
            appProcessInfo.importance == IMPORTANCE_BACKGROUND) {
        return false
    }

    val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val foregroundTaskInfo = am.getRunningTasks(1)[0]
    val foregroundTaskPackageName = foregroundTaskInfo.topActivity.packageName
    return foregroundTaskPackageName.toLowerCase() == context.packageName.toLowerCase()
}

and in Manifest

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />
DeniSHow
  • 1,394
  • 1
  • 18
  • 30