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
Related
It seems Google finally closed all doors for getting the current foreground application package.
After the Lollipop update, which killed getRunningTasks(int maxNum) and thanks to this answer, I used this code to get the foreground application package since Lollipop:
final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) {
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
app.importanceReasonCode == 0 ) {
Integer state = null;
try {
state = field.getInt( app );
} catch (Exception ignored) {
}
if (state != null && state == PROCESS_STATE_TOP) {
currentInfo = app;
break;
}
}
}
return currentInfo;
Android 5.1.1 and above (6.0 Marshmallow), it seems, killed getRunningAppProcesses() as well. It now returns a list of your own application package.
UsageStatsManager
We can use the new UsageStatsManager API as described here but it doesn't work for all applications. Some system applications will return the same package
com.google.android.googlequicksearchbox
AccessibilityService (December 2017: Going to be banned for use by Google)
Some applications use AccessibilityService (as seen here) but it has some disadvantages.
Is there another way of getting the current running application package?
To get a list of running processes on Android 1.6 - Android 6.0 you can use this library I wrote: https://github.com/jaredrummler/AndroidProcesses The library reads /proc to get process info.
Google has significantly restricted access to /proc in Android Nougat. To get a list of running processes on Android Nougat you will need to use UsageStatsManager or have root access.
Click the edit history for previous alternative solutions.
private String printForegroundTask() {
String currentApp = "NULL";
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
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("adapter", "Current App in foreground is: " + currentApp);
return currentApp;
}
Use this method for getting foreground task.
U will need an System Permission "android:get_usage_stats"
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;
}
}
IF user enable this in setting -> Security-> app with usage access. After that u will get foreground task. Similar process Clean matser by Cheetahamobile
google play link
Take a look at https://github.com/ricvalerio/foregroundappchecker, it might be what you need. Provides sample code, and takes away the pain of having to implement cross version foreground detector.
Here are two samples:
AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();
Or regularly check:
AppChecker appChecker = new AppChecker();
appChecker
.when("com.other.app", new AppChecker.Listener() {
#Override
public void onForeground(String packageName) {
// do something
}
)
.when("com.my.app", new AppChecker.Listener() {
#Override
public void onForeground(String packageName) {
// do something
}
)
.other(new AppChecker.Listener() {
#Override
public void onForeground(String packageName) {
// do something
}
)
.timeout(1000)
.start(this);
Google limited this functionality for system apps only. As been reported in a bug ticket, you will need the REAL_GET_TASKS permission to access there.
Applications must now have ...permission.REAL_GET_TASKS to be able to
get process information for all applications. Only the process
information for the calling application will be returned if the app
doesn't have the permission. Privileges apps will temporarily be able
to get process information for all applications if they don't have the
new permission, but have deprecated ...permission.GET_TASKS Also,only
system apps can acquire the REAL_GET_TASKS permission.
Just throwing out a potential optimization to what I imagine is a heavily copy-pasted bit of code for detecting the top-most application on Android M.
This
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
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();
}
}
}
Can be simplified to this
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
time - 1000 * 1000, time);
if (appStatsList != null && !appStatsList.isEmpty()) {
currentApp = Collections.max(appStatsList, (o1, o2) ->
Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
}
}
I found myself using this code in a 2 second loop, and wondered why I was using a complex solution that was O(n*log(n)) when a more simple solution was available in Collections.max() which is O(n).
public class AccessibilityDetectingService extends AccessibilityService {
#Override
protected void onServiceConnected() {
super.onServiceConnected();
//Configure these here for compatibility with API 13 and below.
AccessibilityServiceInfo config = new AccessibilityServiceInfo();
config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
if (Build.VERSION.SDK_INT >= 16)
//Just in case this helps
config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
setServiceInfo(config);
}
#Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
if (event == null ) {
return;
} else if(event.getPackageName() == null && event.getClassName() == null){
return;
}
if (activityInfo != null){
Log.d("CurrentActivity", componentName.flattenToShortString());
}
}
private ActivityInfo tryGetActivity(ComponentName componentName) {
try {
return getPackageManager().getActivityInfo(componentName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
#Override
public void onInterrupt() {
}
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />
Then start the service and app accessibility on in your device
setting->accessibility->App
on that service.
Please try to use getRunningServices() instead of getRunningAppProcesses() method.
ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
How to detect which app has been launched by user in my app i.e my application should get notified when Whatsapp is launched by user even if my app is not running in foreground or background.
hike messenger has achieved same functionality with accessibility service.
How can I solve this problem ?
Thanks in advance!!
Depending on the Android version running your application, you will have to use different methods.
On Pre-Lollipop devices, it is pretty straight-forward:
String[] result = new String[2];
List<ActivityManager.RunningTaskInfo> runningTasks;
ComponentName componentInfo;
runningTasks = activityManager.getRunningTasks(1);
componentInfo = runningTasks.get(0).topActivity;
result[0] = componentInfo.getPackageName();
result[1] = componentInfo.getClassName();
If you are on a Lollipop or newer device, you have to use UsageStatsManager class, which requires your application to be granted specific permissions
//no inspection ResourceType
UsageStatsManager mUsageStatsManager = (UsageStatsManager)context.getSystemService("usagestats");
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<>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(),usageStats);
}
if(mySortedMap != null && !mySortedMap.isEmpty()) {
return mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
return null;
This will tell you if your apps has been granted permissions:
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;
}
And finally this will launch the Android permission granting activity for the user:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
activity.startActivity(intent);
Hope that helps
Try this code:
ActivityManager activityManager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
List<RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
for(int i = 0; i < procInfos.size(); i++)
{
if(procInfos.get(i).processName.equals("put the package name here"))
{
Toast.makeText(getApplicationContext(), "Notify Message", Toast.LENGTH_LONG).show();
}
}
No, this is not really possible using the public SDK.
You can get the current running process by ActivityManager#getRunningAppProcesses But it is definitely impossible to get notified .However, it isn't the most accurate, or efficient method
I want to access the Data usage in android phone at application level.
In my phone with lollipop 5.1, it gives a beautiful graph of the data usage & settings to limit the usage. I think this feature came from Kitkat update.
I want this record, if there is no Public API, is there any other way to do that ?
First, to get the data, you should use either UsageStatsManager or ActivityManager, depending on which SDK version you're using. Here is a simple example to get the name of the currently running application:
String currentApp = "NULL";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager usm = (UsageStatsManager) activity.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 activityManager = (ActivityManager) currentlyDisplayedScreen.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
currentApp = services.get(0).topActivity.getPackageName().toString()
}
These classes can be used to obtain other useful data as well, including how much time each app was in foreground. If you use this, you will need the following permissions:
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
I don't know if you can copy the usage statistics chart itself, but I've used Android's canvas to draw all sorts of stuff, including colorful charts displaying data of different categories. You can create a custom View which overrides the onDraw() method to draw your chart based on the current data you have:
public class StatisticsChartView extends View {
...
#Override
protected void onDraw(Canvas canvas) {
if (values != null) {
float sliceStartPoint = 200;
path.addCircle(rectF.centerX(), rectF.centerY(), 250, Direction.CW);
canvas.clipPath(path, Op.DIFFERENCE);
...
}
My assumption is that you want to have the chart inside your own application, but if you want you can simply launch the Android data usage screen of the device with a proper intent:
Which Intent for Settings - Data usage
However, then you're just navigating to an external screen.
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
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:
How I should run my application in the background.
How my background application can know what the application currently running in the foreground is.
Responses from folks with experience would be greatly appreciated.
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" />
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.
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;
}
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);
}
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.
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);
}
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();
}
}
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.
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());
}
}
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.
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;
}
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;
}
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();
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" />