Determining the current foreground application from a background task or service
Asked Answered
E

14

117

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.

Erfurt answered 30/1, 2010 at 5:24 Comment(3)
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.Cidevant
codingaffairs.blogspot.com/2016/05/…Agave
for detecting the foreground app, you can use github.com/ricvalerio/foregroundappcheckerNewmodel
S
105

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" />
Scanderbeg answered 1/10, 2012 at 14:34 Comment(7)
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.Ryder
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.Ryder
Note: getRunningTasks() is deprecated in API 21 (Lollipop) - developer.android.com/reference/android/app/…Gazehound
@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?Megagamete
There is a way use "Usage statistics api" introduced in 21Macilroy
The first in the list of RunningTasks is always the foreground task. it is false in marshmellowVioloncellist
I made this utility class that solves this issue without any special permissions or API level restrictions gist.github.com/pantos27/6faad313ba364c947e2487e80aa25c86Tiddlywinks
R
39

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.

Rally answered 28/12, 2010 at 5:55 Comment(5)
From Android version L onwards, this is the only solution that works as the methods used in the other solutions has become deprecated.Station
however, this not working in case that app is in foreground and screen is lockAnnoy
what variable is newContext supposed to be?Directorial
Does it needs additional permission like accepted answer?Erectile
This isn't quite correct. A process's name is not always the same as the corresponding app package name.Czarevna
H
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;
}
Heap answered 3/4, 2011 at 8:17 Comment(4)
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?Brittaney
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.Damarisdamarra
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.Alviani
Does anyone know how this approach is better than simply doing mActivityManager.getRunningTasks(1).get(0).topActivity?Czarevna
M
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);
}
Melbamelborn answered 11/6, 2015 at 10:28 Comment(6)
cant this usage stats be set programmatically?Marchant
@Marchant Have you got any solution on this?Submit
in my doogee with android 5.1 there is no option "Give the permissions to our app"Newmarket
This doesn't produce 100% accurate results... it's right most of the timeOe
@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?Czarevna
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.Furan
N
11

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.

Newmodel answered 28/8, 2016 at 22:59 Comment(2)
perfect!! tested on android 7Isopropyl
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!Quinquepartite
A
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);
}
Aun answered 8/3, 2018 at 20:16 Comment(4)
That's what I've been looking for since the past two hours. Thank you! :)Cenis
We need to call "registerActivityLifecycleCallbacks(this);" inside Override method "onCreate()" of Application class (TestApplication). This will start the callbacks in our application class.Weak
@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.Clapper
@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.Aun
C
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();
        }
    }
Clapper answered 18/1, 2017 at 17:4 Comment(2)
you need to mention to add to the manifest the deprecated permission <uses-permission android:name="android.permission.GET_TASKS" /> otherwise app will crashCalabresi
android.permission.GET_TASKS - is currently forbidden in Play StoreJordain
C
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.

Cidevant answered 30/1, 2010 at 5:35 Comment(0)
S
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());
                    }               
                }
Shillelagh answered 15/12, 2014 at 12:0 Comment(1)
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.Dicta
L
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.

Lewak answered 5/5, 2021 at 13:50 Comment(0)
I
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;
}
Incongruity answered 23/2, 2010 at 9:52 Comment(4)
Google will probably reject an app that uses ActivityManager.getRunningTasks(). The documentation states that it is foe dev purposes only : developer.android.com/reference/android/app/… See the intial comment : #4414671Carrier
@Carrier - 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.Windjammer
@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.Carrier
"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 :( )Hotel
M
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;
}
Marchant answered 24/1, 2016 at 5:55 Comment(1)
It is deprecated into marshmallowUlysses
A
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();

Agave answered 24/5, 2016 at 12:51 Comment(1)
do you have a link for this?Playacting
G
-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" />
Graticule answered 9/8, 2017 at 17:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.