Checking if an application is running in foreground - android

In one of my application, I need to launch manually an application thanks to an Intent (which is not mine). After that, I want to know if this application launched by an Intent is running in foreground, in background or killed.
I founded a lot of example and snippets showing how to get the list of running processes/applications but they are calling methods whose are now deprecated since API 21 like ActivityManager.RunningTasks(..) (http://developer.android.com/reference/android/app/ActivityManager.html).
The others methods like ActivityManager.RunningAppProcessInfo only return my current application and not all the other. I understand that this is a security issue of the access to personal information, but is there no other way ?
EDIT 1
After looking at the Android API, I found the class UsageStatsManager and judging by the following post, it seems to be an alternative : How to get list of recent apps with Android API 21 Lollipop?. I tried using UsageStatsManager but the result isn't satisfying. I've got a service which aim to regularly verify if the selected application is in foreground.
public class CastService extends Service
{
private static final String TAG = "CastService";
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
ResolveInfo launchable = (ResolveInfo)intent.getExtras().get("selectedApp");
final String packageName = launchable.activityInfo.packageName;
final Handler handler = new Handler();
final int delay = 2000; // ms
handler.postDelayed(new Runnable()
{
#Override
public void run()
{
Log.i(TAG, "isAppInactive(" + packageName + ") : " + isAppInactive(packageName));
handler.postDelayed(this, delay);
}
}, delay);
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
protected boolean isAppInactive(String packageName)
{
UsageStatsManager usageStatsManager = (UsageStatsManager)getSystemService(Context.USAGE_STATS_SERVICE);
return usageStatsManager.isAppInactive(packageName);
}
}
The service is working fine and log every two seconds the result of the method isAppInactive. Unfortunatetly, even if the concerned application is in background or killed, the result doesn't changed :
I/CastService: isAppInactive(com.sec.android.gallery3d) : false
I thought I forget to add the permission in the AndroidManifest.xml but it doesn't fix it.
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
EDIT 2
Looking at another example (), I rewrote my isAppForeground function but still not working. My list of UsageStats is always empty.
protected boolean isAppForeground(String packageName)
{
UsageStatsManager usageStatsManager = (UsageStatsManager)getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List<UsageStats> stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
String topPackageName = new String();
if(stats != null)
{
SortedMap<Long,UsageStats> mySortedMap = new TreeMap<>();
for (UsageStats usageStats : stats)
{
mySortedMap.put(usageStats.getLastTimeUsed(),usageStats);
}
if(mySortedMap != null && !mySortedMap.isEmpty())
{
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
return topPackageName.equals(packageName);
}
Thanks in advance.

Looking at my application settings, there was no trace of permission, so I decided to check if the permission PACKAGE_USAGE_STATS was really granted thanks to the following function.
public static boolean isPermissionGranted(Context context, String permission)
{
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(permission, android.os.Process.myUid(), context.getPackageName());
boolean granted = mode == AppOpsManager.MODE_ALLOWED;
Log.i(TAG, "isPermissionGranted(" + permission + ", " + android.os.Process.myUid() + ", " + context.getPackageName() + ") : " + granted);
return granted;
}
I was testing my application in debug mode and it didn't ask me to allow the previous permission. Thanks to the terminal and adb command line, I granted the permission manually and my function isAppForeground defined in previous EDIT is finally working fine.
adb -d shell pm grant com.package.name android.permission.PACKAGE_USAGE_STATS

Related

AndroidTest with PACKAGE_USAGE_STATS permission

Our goal is to use AndroidTest(AndroidJUnit4) to run automatic tests.
We have used this code to grant permissions when SDK >= 23
public static void grantPermission(String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!hasPermission(permission))
getInstrumentation().getUiAutomation()
.executeShellCommand("pm grant " + getTargetContext().getPackageName() + " " + permission);
}
}
#Before
public void setUp() throws Exception {
grantPermission("android.permission.ACCESS_NETWORK_STATE");
grantPermission("android.permission.CHANGE_NETWORK_STATE");
grantPermission("android.permission.CAMERA ");
grantPermission("android.permission.INTERNET");
grantPermission("android.permission.READ_EXTERNAL_STORAGE ");
grantPermission("android.permission.WRITE_EXTERNAL_STORAGE");
grantPermission("android.permission.PACKAGE_USAGE_STATS");
}
The method is fine for
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.CAMERA
Although in the Settings/Security/Apps with usage access, our app is on, but when we use this code to check permission, it still doesn't have that permission.
AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
// return MODE_DEFAULT, not MODE_ALLOWED
int result = appOps.checkOpNoThrow("android:get_usage_stats", android.os.Process.myUid(), context.getPackageName())
How can I do to succeed this goal?
We have tried many approved. Finally, we succeeded.
The approved was use "adb shell input tap [x], [y]" to simulate tap, so it is like human tap the screen to trigger the switch.
This is the code us use.
private void grantPermission() throws InterruptedException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Context context = InstrumentationRegistry.getTargetContext();
final AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// check if the app doesn't have permission
if (appOps.checkOpNoThrow("android:get_usage_stats", android.os.Process.myUid(), context.getPackageName()) != AppOpsManager.MODE_ALLOWED) {
UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
//Open UsageAccessSettingsActivity
automation.executeShellCommand("am start com.android.settings/.Settings$UsageAccessSettingsActivity");
Thread.sleep(1000);
//Open the setting of the first app
automation.executeShellCommand(String.format("input tap %s %s", dpToPx(100), dpToPx(138)));
Thread.sleep(1000);
//Tap permit usage access
automation.executeShellCommand(String.format("input tap %s %s", dpToPx(100), dpToPx(164)));
Thread.sleep(1000);
}
}
}
public static int dpToPx(int dp) {
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
We have tried 6 AVDs on SDK 23 and 24 with hdip, xhdip and xxhdip. All works.
UPDATED[2/10]:
We found another easier way to do it. use "adb shell appops" commend.
This is ours code.
#Before
public void setUp() throws Exception {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Context context = InstrumentationRegistry.getTargetContext();
final AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (appOps.checkOpNoThrow("android:get_usage_stats", android.os.Process.myUid(), context.getPackageName()) != AppOpsManager.MODE_ALLOWED) {
InstrumentationRegistry
.getInstrumentation()
.getUiAutomation()
.executeShellCommand("appops set " + context.getPackageName() + " android:get_usage_stats allow");
}
}
}
Make sure your app has usage stats enabled. You can check this by launching the Usage Stats Activity. add this to your code on your onCreate method or possible on a button click:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

Get foreground activity from package name?

I am trying to get running activity name (Foreground activity name) from package name as you all know that we can get currently running activity name
via this line
mActivityManager.getRunningTasks(1).get(0).topActivity.getClassName()
but this work only bellow android 5.0 i need same thing in above 5.0
i am able to get current package name but not activity
security is enhanced from android Lollipop; see this article for more details: getAppTask
You could get foreground activities via UsageStats class; first, you have to add this permission in your manifest:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
and this declaration:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your_application_package">
This will disable warning that permission is system level and will not be granted to third-party apps. User of the device can grant permission through the Settings application.
So, in onCreate() method of your MainActivity, check if user granted this permission
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
if(!hasPermission()){
startActivityForResult(
new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),
Constant.MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);
}
}
And if no, ask user to enable it:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Constant.MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS){
if (!hasPermission()){
startActivityForResult(
new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),
Constant.MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);
}
}
}
private boolean hasPermission() {
AppOpsManager appOps = (AppOpsManager)
getSystemService(Context.APP_OPS_SERVICE);
int mode = 0;
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.KITKAT) {
mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), getPackageName());
}
return mode == AppOpsManager.MODE_ALLOWED;
}
Finally, here's a snippet to put in a background service, that gives you package name of current foreground activites:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private String getUsageStatsForegroundActivityName() {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) MyApplication.getInstance().getSystemService(Context.USAGE_STATS_SERVICE);
long endTime = System.currentTimeMillis();
long beginTime = endTime - 1000 * 60;
// result
String topActivity = null;
// We get usage stats for the last minute
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, beginTime, endTime);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
topActivity = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
logger.info("topActivity: " + topActivity);
}
}
if (topActivity != null)
return topActivity;
else
return Constant.ACTIVITY_NOT_FOUND;
}

Check if my application has usage access enabled

I'm using the new UsageStatsManager API to get current foreground application in Android 5.0 Lollipop.
In order to use this API, the user must enable the application in the Settings->Security->Apps with usage access screen.
I send the user directly to this screen with this Intent:
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
Now, I want to validate the user enabled my application.
I wanted to do so like I validate the user enabled my application to use the NotificationListenerService but I have no idea what is the String key, if it even exists.
Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
// Tried Settings.ACTION_USAGE_ACCESS_SETTINGS as key but it returns null
Second approach was to query the usage stats and check if it returns results (it returns an empty array when the app is not enabled) and it works most of the times but sometimes it returns 0 results even when my app is enabled.
UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService("usagestats");
long time = System.currentTimeMillis();
List stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
if (stats == null || stats.isEmpty()) {
// Usage access is not enabled
}
Is there a way to check if my application has usage access enabled?
Received a great answer by someone on Twitter, tested working:
try {
PackageManager packageManager = context.getPackageManager();
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
return (mode == AppOpsManager.MODE_ALLOWED);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
I previously used the same code as Bao Le, but I've run into the problem that certain devices (e.g. VF-895N) report usage stats as enabled even when they're not. As a workaround I've modified my code like this:
public static boolean hasPermission(#NonNull final Context context) {
// Usage Stats is theoretically available on API v19+, but official/reliable support starts with API v21.
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
return false;
}
final AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (appOpsManager == null) {
return false;
}
final int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
if (mode != AppOpsManager.MODE_ALLOWED) {
return false;
}
// Verify that access is possible. Some devices "lie" and return MODE_ALLOWED even when it's not.
final long now = System.currentTimeMillis();
final UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
final List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, now - 1000 * 10, now);
return (stats != null && !stats.isEmpty());
}
Successfully tested on multiple devices.
Here's my all-around solution for this (based on similar question and answer here) :
public static PermissionStatus getUsageStatsPermissionsStatus(Context context) {
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
return PermissionStatus.CANNOT_BE_GRANTED;
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
final int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
boolean granted = mode == AppOpsManager.MODE_DEFAULT ?
(context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED)
: (mode == AppOpsManager.MODE_ALLOWED);
return granted ? PermissionStatus.GRANTED : PermissionStatus.DENIED;
}
public enum PermissionStatus {
GRANTED, DENIED, CANNOT_BE_GRANTED
}
Detecting when the usage access changes
Use this class to be notified when your app is granted or revoked usage access.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class UsagePermissionMonitor {
private final Context context;
private final AppOpsManager appOpsManager;
private final Handler handler;
private boolean isListening;
private Boolean lastValue;
public UsagePermissionMonitor(Context context) {
this.context = context;
appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
handler = new Handler();
}
public void startListening() {
appOpsManager.startWatchingMode(AppOpsManager.OPSTR_GET_USAGE_STATS, context.getPackageName(), usageOpListener);
isListening = true;
}
public void stopListening() {
lastValue = null;
isListening = false;
appOpsManager.stopWatchingMode(usageOpListener);
handler.removeCallbacks(checkUsagePermission);
}
private final AppOpsManager.OnOpChangedListener usageOpListener = new AppOpsManager.OnOpChangedListener() {
#Override
public void onOpChanged(String op, String packageName) {
// Android sometimes sets packageName to null
if (packageName == null || context.getPackageName().equals(packageName)) {
// Android actually notifies us of changes to ops other than the one we registered for, so filtering them out
if (AppOpsManager.OPSTR_GET_USAGE_STATS.equals(op)) {
// We're not in main thread, so post to main thread queue
handler.post(checkUsagePermission);
}
}
}
};
private final Runnable checkUsagePermission = new Runnable() {
#Override
public void run() {
if (isListening) {
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, Process.myUid(), context.getPackageName());
boolean enabled = mode == AppOpsManager.MODE_ALLOWED;
// Each change to the permission results in two callbacks instead of one.
// Filtering out the duplicates.
if (lastValue == null || lastValue != enabled) {
lastValue = enabled;
// TODO: Do something with the result
Log.i(UsagePermissionMonitor.class.getSimpleName(), "Usage permission changed: " + enabled);
}
}
}
};
}
Credits
Based on code from epicality in another answer.
This is an alternative solutions:
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), context.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED;
This works down to KitKat (API 19)
AppOpsManager appOps = (AppOpsManager) context
.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow("android:get_usage_stats",
android.os.Process.myUid(), context.getPackageName());
boolean granted = mode == AppOpsManager.MODE_ALLOWED;
None of the answer worked for me so i made this
public boolean permissiontodetectapp(Context context) {
try {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
return ((AppOpsManager) context.getSystemService(APP_OPS_SERVICE)).checkOpNoThrow("android:get_usage_stats", applicationInfo.uid, applicationInfo.packageName) != 0;
} catch (PackageManager.NameNotFoundException unused) {
return true;
}
}
this code working in lollipop and marshmallow i used this code in my app
if (Build.VERSION.SDK_INT >= 21) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
if (stats == null || stats.isEmpty()) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_USAGE_ACCESS_SETTINGS);
context.startActivity(intent);
}
}
If they are using an Amazon Fire tablet (and possibly other Fire OS devices) the user can download the application from a user installed Google Play Store then not have the option you want activated available in their OS. I know this because as a Fire OS user this happened to me a few minutes ago. Detecting whether a user has Fire OS and, if so, offering an option which actually exists would be fantastic for both user and dev.
try this ,
public boolean check_UsgAccs(){
long tme = System.currentTimeMillis();
UsageStatsManager usm = (UsageStatsManager)getApplicationContext().getSystemService(Context.USAGE_STATS_SERVICE);
List<UsageStats> al= usm.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, tme - (1000 * 1000), tme);
return al.size()>0;
}

Is there an alternative for getRunningTask API

I was using getRunningTask API in one of my application to find the Foreground application. This API has been deprecated since Lollipop. After this deprecation, I preferred getRunningAppProcess API along with Importance_Foreground. I also ruled out REASON_SERVICE and REASON_PROVIDER from this list. I filtered out the system applications based on a logic, which worked perfectly. The problem is that, If Application A is on foreground, I get Application B as a spike. So, this approach is currently questionable. Is there any other alternative to the getRunningTask API?? Or am I missing any simple thing in the current approach. Please help guys.
Based on the answer to this question
String getTopPackage(){
long ts = System.currentTimeMillis();
UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService("usagestats");
List<UsageStats> usageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts-1000, ts);
if (usageStats == null || usageStats.size() == 0) {
return NONE_PKG;
}
Collections.sort(usageStats, mRecentComp);
return usageStats.get(0).getPackageName();
}
This is the mRecentComp:
static class RecentUseComparator implements Comparator<UsageStats> {
#Override
public int compare(UsageStats lhs, UsageStats rhs) {
return (lhs.getLastTimeUsed() > rhs.getLastTimeUsed()) ? -1 : (lhs.getLastTimeUsed() == rhs.getLastTimeUsed()) ? 0 : 1;
}
}
This permission is needed:
<uses-permission xmlns:tools="http://schemas.android.com/tools"
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
And you will need user authorization to request the stats, use this to direct the user to the settings page:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
And you can check if you already have permission like this:
public static boolean needPermissionForBlocking(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
return (mode != AppOpsManager.MODE_ALLOWED);
} catch (PackageManager.NameNotFoundException e) {
return true;
}
}
Get the list of RunningAppProcessInfo by ActivityManager.getRunningAppProcesses().
Choose the RuningAppProcessInfo with whose importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND and whose processState == ActivityManager.START_TASK_TO_FRONT.
(The former is easy, the latter is difficult because the reflection is necessary)
See my answer for this question getRunningTasks doesn't work in Android L

getRunningTasks doesn't work in Android L

In Android L, Google has disabled getRunningTasks. Now it can only return own apps task and the home launcher. I can no longer get other apps tasks.
Our app needs that method to determine current top app.
Any one has another method to do this?
I have searched in Google, no more topics about this except this:
https://code.google.com/p/android-developer-preview/issues/detail?id=29
For a recent project that I worked on, I also need to detect when certain applications are launched. All my research lead to the getRunningTasks method, which is deprecated starting from Lollipop.
However, to my surprises, I discovered that some of the app lock apps still work on lollipop devices, so they must have come up with a solution to get around this. So I dug a little deeper. Here is what I found out:
On pre-L devices, they still use getRunningTasks
On L devices, they use getRunningAppProcesses, which returns a list of processes currently running on the devices. You might think "well, that is not useful". Each processInfo has a attributed called importance. When an app becomes top activity, its processInfo importance also changes to IMPORTANCE_FOREGROUND. So you can filter out those processes that are not in foreground. From a each ProcessInfo, you can also ask a list of packages it loaded. You can then check if the list contains the same package that the app when are trying "protected".
Some sample code to detect when the default calendar app is launched:
public class DetectCalendarLaunchRunnable implements Runnable {
#Override
public void run() {
String[] activePackages;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
activePackages = getActivePackages();
} else {
activePackages = getActivePackagesCompat();
}
if (activePackages != null) {
for (String activePackage : activePackages) {
if (activePackage.equals("com.google.android.calendar")) {
//Calendar app is launched, do something
}
}
}
mHandler.postDelayed(this, 1000);
}
String[] getActivePackagesCompat() {
final List<ActivityManager.RunningTaskInfo> taskInfo = mActivityManager.getRunningTasks(1);
final ComponentName componentName = taskInfo.get(0).topActivity;
final String[] activePackages = new String[1];
activePackages[0] = componentName.getPackageName();
return activePackages;
}
String[] getActivePackages() {
final Set<String> activePackages = new HashSet<String>();
final List<ActivityManager.RunningAppProcessInfo> processInfos = mActivityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
activePackages.addAll(Arrays.asList(processInfo.pkgList));
}
}
return activePackages.toArray(new String[activePackages.size()]);
}
}
Note: getRunningAppProcesses is also intended for debugging or "building a user-facing process management UI". Not sure if google will close this backdoor the similar way they did to getRunningTasks.
So no, you can't get the topActivity anymore. But with a little bit hack you can achieve similar result.
As MKY mentioned, getRunningTasks() method does not work for getting the current application in Lollipop.
As sunxin8086 wrote, the one way for getting the running applications is by using getRunningAppsProcesses() method. However, the condition info.importance == IMPORTANCE_FOREGROUND can not determine the current app uniquely.
The better approach to determine the current foreground application may be checking the processState field in RunningAppProcessInfo object. This field is a hidden field, but you can see it in the RunningAppProcessInfo class. If this value is ActivityManager.PROCESS_STATE_TOP (which is also
hidden static constant), the process is the current foreground process.
For example the code is
final int PROCESS_STATE_TOP = 2;
ActivityManager.RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) {
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo app : appList) {
if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& app.importanceReasonCode == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
Integer state = null;
try {
state = field.getInt(app);
} catch (Exception e) {
}
if (state != null && state == PROCESS_STATE_TOP) {
currentInfo = app;
break;
}
}
}
return currentInfo;
Note: processState field does not exist in pre-Lolipop. Please check that Build.VERSION.SDK_INT >= 21 before running the above code. The above code works only for Lollipop+.
The other approach, by Gaston (which is quite different), and the meaning of 'current application' is slightly different from this approach.
Please choose one for your purpose.
[EDIT]
As Sam pointed out, I modified START_TASK_TO_FRONT by PROCESS_STATE_TOP. (Both values are 2)
[EDIT2]
Sam has a new find! To determine the foreground application uniquely, one more
condition
process.importanceReasonCode == 0
is necessary. The above code has been updated. Thanks!
Here's an exact solution to get current top activity on your Android L/Lollipop devices and Android M/Marshmallow devices.
First call this line of code:(One time)
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
The above code will open a screen named "Apps with usage access". Just check the radio button to on/true to allow usage access.
Now call the following method in your service or anywhere you want:
public void getTopActivtyFromLolipopOnwards() {
String topPackageName;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
// We get usage stats for the last 10 seconds
List < UsageStats > stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap < Long, UsageStats > mySortedMap = new TreeMap < Long, UsageStats > ();
for (UsageStats usageStats: stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
Log.e("TopPackage Name", topPackageName);
}
}
}
}
add permission
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
This will return the package name of currently running activity , whether it is facebook or whatsapp.
The only complication of this method is you need to prompt user for allowing app usage stats ... i.e. the first step.
Hope! this helps everyone.
private String getProcess() throws Exception {
if (Build.VERSION.SDK_INT >= 21) {
return getProcessNew();
} else {
return getProcessOld();
}
}
//API 21 and above
private String getProcessNew() throws Exception {
String topPackageName = null;
UsageStatsManager usage = (UsageStatsManager) context.getSystemService(Constant.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List<UsageStats> stats = usage.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - ONE_SECOND * 10, time);
if (stats != null) {
SortedMap<Long, UsageStats> runningTask = new TreeMap<Long,UsageStats>();
for (UsageStats usageStats : stats) {
runningTask.put(usageStats.getLastTimeUsed(), usageStats);
}
if (runningTask.isEmpty()) {
return null;
}
topPackageName = runningTask.get(runningTask.lastKey()).getPackageName();
}
return topPackageName;
}
//API below 21
#SuppressWarnings("deprecation")
private String getProcessOld() throws Exception {
String topPackageName = null;
ActivityManager activity = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> runningTask = activity.getRunningTasks(1);
if (runningTask != null) {
RunningTaskInfo taskTop = runningTask.get(0);
ComponentName componentTop = taskTop.topActivity;
topPackageName = componentTop.getPackageName();
}
return topPackageName;
}
//required permissions
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
I Think its not possible to get other app's tasks,
This is what documentation says
With the introduction of the new concurrent documents and activities
tasks feature in the upcoming release (see Concurrent documents and
activities in Recents screen below), the
ActivityManager.getRecentTasks() method is now deprecated to improve
user privacy. For backward compatibility, this method still returns a
small subset of its data, including the calling application’s own
tasks and possibly some other non-sensitive tasks (such as Home). If
your app is using this method to retrieve its own tasks, use
android.app.ActivityManager.getAppTasks() instead to retrieve that
information.
Check out the api overview of Android L here https://developer.android.com/preview/api-overview.html#Behaviors

Categories

Resources