31

I am creating a app lock application. How to get current running task in lollipop? getRunningTaskinfo method is deprecated in lollipop API, then how to overcome this problem?

Ana
  • 166
  • 1
  • 16
Rathan Kumar
  • 2,567
  • 2
  • 17
  • 24
  • You could use the UsageStatsManager to query for events (not usage stats), but as per documentation, the last minutes (actually seconds) are cut off to prevent non-system applications to know what application is currently running. – David Corsalini Jan 21 '15 at 12:00

9 Answers9

45

try this:

ActivityManager mActivityManager =(ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);

if(Build.VERSION.SDK_INT > 20){
String mPackageName = mActivityManager.getRunningAppProcesses().get(0).processName;
}
else{
  String mpackageName = mActivityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
}

we can get using UsageStats:

public static String getTopAppName(Context context) {
    ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    String strName = "";
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            strName = getLollipopFGAppPackageName(context);
        } else {
            strName = mActivityManager.getRunningTasks(1).get(0).topActivity.getClassName();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return strName;
}


private static String getLollipopFGAppPackageName(Context ctx) {

    try {
        UsageStatsManager usageStatsManager = (UsageStatsManager) ctx.getSystemService("usagestats");
        long milliSecs = 60 * 1000;
        Date date = new Date();
        List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, date.getTime() - milliSecs, date.getTime());
        if (queryUsageStats.size() > 0) {
            Log.i("LPU", "queryUsageStats size: " + queryUsageStats.size());
        }
        long recentTime = 0;
        String recentPkg = "";
        for (int i = 0; i < queryUsageStats.size(); i++) {
            UsageStats stats = queryUsageStats.get(i);
            if (i == 0 && !"org.pervacio.pvadiag".equals(stats.getPackageName())) {
                Log.i("LPU", "PackageName: " + stats.getPackageName() + " " + stats.getLastTimeStamp());
            }
            if (stats.getLastTimeStamp() > recentTime) {
                recentTime = stats.getLastTimeStamp();
                recentPkg = stats.getPackageName();
            }
        }
        return recentPkg;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}

// TO ENABLE USAGE_STATS

    // Declare USAGE_STATS permisssion in manifest

    <uses-permission
    android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions" />


    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    startActivity(intent);
KomalG
  • 808
  • 9
  • 18
  • This returns the process name instead of the package name. They're sometimes different. – Sam May 16 '15 at 03:15
  • This seems to use the base activity instead of the top activity for API 20-. Shouldn't it be the top activity? – Sam May 16 '15 at 03:16
  • 1
    I think it returns the application base package name . – WenChao Jul 28 '15 at 04:59
  • @WenChao, note the bit in the code that says `.processName`. The default process name for an app's components is the app's package name. However, an app can specify a different process name for its components. The correct way to get the packages associated with a process is to use the `.pkgList` property. – Sam Nov 04 '16 at 22:06
  • @Sam I'm facing difficulties as you mentioned in first comment . got different name rather than package name above 21. Is there any work around ? – Tejas Pandya Jan 17 '18 at 07:50
  • @Tej, see https://stackoverflow.com/questions/3873659/android-how-can-i-get-the-current-foreground-activity-from-a-service for a number of options. But the simple answer is that this is not supported by Android and you shouldn't do it. – Sam Jan 17 '18 at 08:13
  • @Tej, oh, it looks like I found a workaround in [my second comment](https://stackoverflow.com/questions/28066231/how-to-gettopactivity-name-or-get-current-running-application-package-name-in-lo/28066580?noredirect=1#comment68113245_28066580). – Sam Jan 17 '18 at 08:14
12

Best solution of get Running app in API 21 or up is below try it. this work for me

private String retriveNewApp() {
    if (VERSION.SDK_INT >= 21) {
        String currentApp = null;
        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<>();
            for (UsageStats usageStats : applist) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
        Log.e(TAG, "Current App in foreground is: " + currentApp);

        return currentApp;

    }
    else {

        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        String mm=(manager.getRunningTasks(1).get(0)).topActivity.getPackageName();
        Log.e(TAG, "Current App in foreground is: " + mm);
        return mm;
    }
}
Manish Godhani
  • 189
  • 1
  • 5
8

You can use the AccessibilityService to get current Running app. Accessibility Service provides the onAccessibilityEvent event.

Following is some sample code.

@Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.getPackageName() != null && event.getClassName() != null) {
                Log.d("Foreground App", event.getPackageName().toString());  

            }
        }
    }

You can find more info about Accessibility Service here

Juli
  • 1,011
  • 8
  • 16
Sarthak Doshi
  • 424
  • 4
  • 8
  • Unfortunately it may be incorrect. e.g. launcher app can be triggered suddenly after appearing of the foreground app – Vlad Mar 28 '21 at 17:08
7

according to this ; the following code worked perfectly for me :

MOVE_TO_FOREGROUND and MOVE_TO_BACKGROUND added in sdk 21 and deprecated in sdk 29

ACTIVITY_RESUMED and ACTIVITY_PAUSED added in sdk 29

public static String getTopPkgName(Context context) {
    String pkgName = null;

    UsageStatsManager usageStatsManager = (UsageStatsManager) context
            .getSystemService(Context.USAGE_STATS_SERVICE);

    final long timeTnterval= 1000 * 600;
    final long endTime = System.currentTimeMillis();
    final long beginTime = endTime - timeTnterval;
    final UsageEvents myUsageEvents = usageStatsManager .queryEvents(beginTime , endTime );
    while (myUsageEvents .hasNextEvent()) {
        UsageEvents.Event myEvent = new UsageEvents.Event();
        myUsageEvents .getNextEvent(myEvent );
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            switch (myEvent .getEventType()) {
                case UsageEvents.Event.ACTIVITY_RESUMED:
                    pkgName = myEvent .getPackageName();
                    break;
                case UsageEvents.Event.ACTIVITY_PAUSED:
                    if (myEvent .getPackageName().equals(pkgName )) {
                        pkgName = null;
                    }
            }
        }else {
            switch (event.getEventType()) {
                case UsageEvents.Event.MOVE_TO_FOREGROUND:
                    pkgName = myEvent .getPackageName();
                    break;
                case UsageEvents.Event.MOVE_TO_BACKGROUND:
                    if (myEvent .getPackageName().equals(pkgName )) {
                        pkgName = null;
                    }
            }
        }
    }

    return pkgName ;
}
Javad Shirkhani
  • 343
  • 6
  • 11
5

Are you sure? As I can see according to the latest Android docs, LOLLIPOP update doesnt allow you to know any info about other apps than your own!?

http://developer.android.com/reference/android/app/ActivityManager.html You can see that all those methods are deprecated!

Dmitry Isakov
  • 580
  • 5
  • 18
2

Try this.. This worked for me.

ActivityManager activityManager = (ActivityManager) getSystemService (Context.ACTIVITY_SERVICE);

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP)    
{

String packageName = activityManager.getRunningAppProcesses().get(0).processName;
} 
else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP)
{
String packageName =  ProcessManager.getRunningForegroundApps(getApplicationContext()).get(0).getPackageName();

}
else
{
String packageName = activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
}
AmAnDroid
  • 246
  • 2
  • 11
  • 6
    what is ProcessManager ? – Nitesh Khosla Aug 29 '16 at 07:46
  • Note that `.processName` isn't always going to be the package name of the process. – Sam Nov 04 '16 at 22:08
  • 3
    I just did a quick Google search, and `ProcessManager` seems to be a deprecated class in the [`AndroidProcesses`](https://github.com/jaredrummler/AndroidProcesses) library. – Sam Nov 04 '16 at 22:12
1

@Manish Godhani's answer (https://stackoverflow.com/a/38829083/8179249) works very well, but you have to give the right permissions for this !

See the two first points of that answer : https://stackoverflow.com/a/42560422/8179249

It works for me (before adding permissions I was getting 'null' too), as shown below : Results given by the code of that answer, after adding permissions

TBG
  • 154
  • 4
  • 18
0

Can be achieved like this.....

ActivityManager am =(ActivityManager)context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
ActivityManager.RunningTaskInfo task = tasks.get(0); // current task
ComponentName rootActivity = task.baseActivity;


rootActivity.getPackageName();//*currently active applications package name*

For lollipop:

ActivityManager mActivityManager =(ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);

if(Build.VERSION.SDK_INT > 20){
    String activityName =mActivityManager.getRunningAppProcesses().get(0).processName;
 }
else{
 String activityName =   mActivityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
}

for complete example check this out...

Gagan
  • 745
  • 10
  • 31
  • [getRunningAppProcesses](https://developer.android.com/reference/android/app/ActivityManager.html#getRunningAppProcesses()) comes with a warning of only using it when in debug mode – Clocker Nov 26 '16 at 00:58
0

There are three ways exist for getting the current activity name on the screen.

Via UsageStatsManager

This way needs an extra permission for the user and the user needs manually enable this permission for your app.

You need to first add permission to your manifest

<uses-permission 
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />

Then redirect the user to enable this permission with this code:

val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
startActivity(intent)

After this permission is added you can get the current activity name with this code:

var lastActivityName: String? = null
private fun getCurrentActivityName(): String? {
    val usageStatsManager =
        applicationContext.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager
    try {
        val currentTimeMillis = System.currentTimeMillis()
        var activityName: String? = null
        var packageName: String? = null
        val queryEvents = usageStatsManager.queryEvents(
            currentTimeMillis - 60000.toLong(),
            currentTimeMillis
        )
        while (queryEvents.hasNextEvent()) {
            val event = UsageEvents.Event()
            queryEvents.getNextEvent(event)
            if (event.eventType == 1) {
                packageName = event.packageName
                activityName = event.className
            }
            else if (event.eventType == 2) {
                if (event.packageName != packageName) {
                    break
                }
                packageName = null
            }
        }
        return if (packageName != null && activityName != null) {
            lastActivityName = activityName
            activityName
        } else lastActivityName
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

I added lastActivityName because you may sometimes get null in the same activity, so you can remove it.

Via Accessibility

This can be possible via Accessibility Service. But since Android 11 you can't retrieve the list of other package names without adding this special permission

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

<queries>
    <intent>
        <action android:name="android.intent.action.MAIN" />
    </intent>
</queries>

But the issue is GooglePlay may reject your app if you have no good explanation.

I will first show you how to get the current activity name with this special permission then will add a trick to how to get activity without adding this permission via only accessibility service.

For setting up the accessibility service you need to add this to res/xml/accessibility_setting.xml (the name of the file can be anything)

<?xml version="1.0" encoding="utf-8"?>

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
   android:accessibilityFeedbackType="feedbackGeneric"
   android:accessibilityFlags="flagDefault|flagReportViewIds|flagIncludeNotImportantViews"
   android:accessibilityEventTypes="typeWindowStateChanged"
   android:canRequestFilterKeyEvents="true"
   android:canRetrieveWindowContent="true" />

Then you need to add your Accessibility service to the Manifest.xml like below:

<service
     android:name=".services.MyAccessibilityService"
     android:label="@string/app_name"
     android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
     android:exported="true">

        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>

        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibility_setting" />
</service>

The next would be defining our MyAccessibilityService service class and the function to retrieve the current activity name:

It should be like that:

class MyAccessibilityService : AccessibilityService() {

   var currentActivityName : String? = null

   override fun onServiceConnected() {
       //Define your initial configs here
   }

   @Synchronized
   override fun onAccessibilityEvent(event: AccessibilityEvent) {
       checkForCurrentActivity(event)
   }

   override fun onInterrupt() {
       // define your interruption code here
   }

Then the checkForCurrentActivity(event) would be:

private fun checkForCurrentActivity(event: AccessibilityEvent) {
    if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
        if (event.packageName != null && event.className != null) {
            val componentName = ComponentName(
                event.packageName.toString(),
                event.className.toString()
            )
            if (isActivity(componentName)) {
                val componentFlattenName = componentName.flattenToShortString()
                currentActivityName = componentFlattenName.replace("/.", ".")
                if (currentActivityName.contains("/")) {
                    currentActivityName = currentActivityName.split("/".toRegex())[1]
                }
            }
        }
    }
}

private fun isActivity(componentName: ComponentName): Boolean {
    return try {
        packageManager.getActivityInfo(componentName, 0)
    } catch (e: PackageManager.NameNotFoundException) {
        null
    } != null
}

And finally, you can easily obtain the current activity name by using currentActivityName

Via Accessibility (with no extra permissions)

In the end, I will show you a hack to get the current activity name just with accessibility permission, without using QUERY_ALL_PACKAGES.

This solution is based on the second solution but with a small difference. For this hack, you need to replace the isActivity(componentName) with:

private fun isActivity(componentName: ComponentName):Boolean {
    val name = componentName.flattenToShortString()
    return name.contains("/") &&
        !name.contains("com.android.systemui") &&
        !name.contains("Layout") &&
        !name.contains(".widget") &&
        !name.contains("android.view") &&
        !name.contains("android.material") &&
        !name.contains("android.inputmethodservice") &&
        !name.contains("$") &&
        !name.contains("android.view") &&
        !name.contains("android.app.dialog")
}

And just like before you can easily obtain the current activity name by using currentActivityName

This hack works with almost 99% of accuracy.

Amir Hossein Ghasemi
  • 20,623
  • 10
  • 57
  • 53