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.
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;
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 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;
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;
}
来源:https://stackoverflow.com/questions/27215013/check-if-my-application-has-usage-access-enabled