【问题标题】:Check if my application has usage access enabled检查我的应用程序是否启用了使用访问权限
【发布时间】:2014-11-30 15:46:31
【问题描述】:

我在 Android 5.0 Lollipop 中使用新的 UsageStatsManager API 到 get current foreground application。 要使用此 API,用户必须在 Settings->Security->Apps with usage access 屏幕中启用应用程序。

我用这个 Intent 将用户直接发送到这个屏幕:


startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

现在,我想验证用户是否启用了我的应用程序。 我想这样做,就像我验证用户启用我的应用程序以使用 NotificationListenerService 但我不知道 String 键是什么,如果它甚至存在的话。


Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
// Tried Settings.ACTION_USAGE_ACCESS_SETTINGS as key but it returns null

第二种方法是查询使用情况统计并检查它是否返回结果(当应用程序未启用时它返回一个空数组)并且它在大多数情况下都可以工作,但有时即使我的应用程序启用它也会返回 0 结果。


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
}

有没有办法检查我的应用程序是否启用了使用访问权限?

【问题讨论】:

  • lluz:你知道如何自动打开吗?
  • @user8430 它写在 OP 中。 startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));您只能将用户发送到屏幕,不能自动为他启用。
  • 我明白了。谢谢你。我发现另一个问题是当用户正确选择应用程序时我们如何关闭 Intent。因为,在我选择应用程序后,我必须按返回按钮才能返回我的 Activity。当用户选择目标应用程序时,我正在寻找一种自动返回活动的自动方法
  • @user8430 在将用户发送到设置屏幕之前使用布尔标志并检查他是否返回 onResume -> 检查是否授予权限。
  • 我在stackoverflow.com/questions/39651734/…发现一个人的问题与我的问题相同

标签: android usage-statistics


【解决方案1】:

在 Twitter 上收到了某人的一个很好的回答,测试工作:

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;
}

【讨论】:

  • 如果用户点击允许我们可以在ActivityResult中得到任何回调
  • @Umar,你说这在 Lollipop 上不起作用,但你是说 KitKat 吗?它不适用于 KitKat,因为 AppOpsManager.OPSTR_GET_USAGE_STATS 仅在 API 级别 21 上受支持。将该常量替换为“android:get_usage_stats”,这将适用于 KitKat。
【解决方案2】:

我之前使用的代码与 Bao Le 相同,但我遇到了某些设备(例如 VF-895N)报告使用情况统计信息为启用的问题,即使它们未启用。作为一种解决方法,我已经像这样修改了我的代码:

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());
}

在多个设备上成功测试。

【讨论】:

    【解决方案3】:

    检测何时使用权限改变

    使用此类在您的应用被授予或撤销使用访问权限时收到通知。

    @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);
                    }
                }
            }
        };
    
    }
    

    学分

    基于来自epicalityanother answer 中的代码。

    【讨论】:

    • 请注意,unsafeCheckOpNoThrow 是 checkOpNoThrow 的新名称(在 Android Q 中使用),因为 checkOpNoThrow 现在已弃用。
    • 为什么不安全?为什么要弃用它?只是为了重命名?
    【解决方案4】:

    这是我的全方位解决方案(基于类似的问答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
    }
    

    【讨论】:

    • 我调查了这段代码中的额外检查,发现它实际上并不需要:stackoverflow.com/a/54847723/238753
    • 为什么要投反对票?这是一种正确的方法,它可以处理所有情况。所有 Android 版本,以及类似的用户/系统应用程序......
    【解决方案5】:

    这是另一种解决方案:

    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;
    

    【讨论】:

      【解决方案6】:

      这适用于 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;
      

      【讨论】:

      • 只是出于好奇,UsageStatsManager 真的适用于 KitKat 吗?我以为它只能从 Lollipop 获得。
      • 是的,使用状态管理器仅适用于 API 22。但以上代码与使用状态管理器无关。
      【解决方案7】:

      没有一个答案对我有用,所以我做了这个

      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;
          }
      }
      

      【讨论】:

        【解决方案8】:

        这个代码在棒棒糖和棉花糖中工作我在我的应用程序中使用了这个代码

        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);
                    }
            }
        

        【讨论】:

          【解决方案9】:

          如果他们使用的是 Amazon Fire 平板电脑(可能还有其他 Fire OS 设备),则用户可以从安装了 Google Play 商店的用户下载应用程序,然后在他们的操作系统中没有您想要激活的选项。我知道这一点,因为作为 Fire OS 用户,这在几分钟前发生在我身上。检测用户是否有 Fire OS,如果有,提供一个实际存在的选项对用户和开发者来说都很棒。

          【讨论】:

            【解决方案10】:

            试试这个,

            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;
            
                }
            

            【讨论】:

              猜你喜欢
              • 2015-09-30
              • 2013-08-11
              • 2013-04-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-09-18
              • 2016-09-12
              相关资源
              最近更新 更多