Проверьте, включено ли в моем приложении доступ к использованию

Я использую новый API UsageStatsManager для получить текущее приложение переднего плана в Android 5.0 Lollipop. Чтобы использовать этот API, пользователь должен включить приложение на экране Settings->Security->Apps with usage access.

Я посылаю пользователя непосредственно на этот экран с этим намерением:


startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

Теперь я хочу подтвердить, что пользователь включил мое приложение. Я хотел сделать так, как будто я проверяю, что пользователь разрешил моему приложению использовать NotificationListenerService, но я понятия не имею, что такое строковый ключ, даже если он существует.


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
}

Есть ли способ проверить, включено ли у моего приложения доступ к использованию?

Ответ 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;
}

Ответ 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 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

Это альтернативные решения:

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;

Ответ 4

Вот мое универсальное решение для этого (на основе аналогичного вопроса и ответа здесь):

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
}

Ответ 5

Это работает до 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;

Ответ 6

Обнаружение, когда изменяется доступ использования

Используйте этот класс, чтобы получать уведомления, когда вашему приложению предоставлен или аннулирован доступ к использованию.

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

}

кредиты

На основании кода из эпичности в другом ответе.

Ответ 7

этот код работает в леденец и зефир, я использовал этот код в своем приложении

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

Ответ 8

Если они используют планшет Amazon Fire (и, возможно, другие устройства Fire OS), пользователь может загрузить приложение у пользователя, установленного в Google Play Store, а затем у вас нет опции, которую вы хотите активировать в своей ОС. Я знаю это, потому что как пользователь Fire OS это случилось со мной несколько минут назад. Обнаружение того, имеет ли пользователь Fire OS, и если да, то предлагаемый вариант, который фактически существует, был бы фантастическим для пользователя и разработчика.

Ответ 9

попробуй это,

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;

    }