web-dev-qa-db-fra.com

Vérifier si l'accès à l'utilisation de mon application est activé

J'utilise la nouvelle API UsageStatsManager pour obtenir l'application de premier plan actuelle dans Android 5.0 Lollipop . Pour utiliser cette API, l'utilisateur doit activer l'application dans l'écran Settings->Security->Apps with usage access.

J'envoie l'utilisateur directement à cet écran avec cette intention:


startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

Maintenant, je veux valider que l'utilisateur a activé mon application . Je voulais le faire comme si je validais l'utilisateur qui avait activé mon application pour utiliser la variable NotificationListenerService .


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

La deuxième approche consistait à interroger les statistiques d'utilisation et à vérifier si elle renvoie des résultats (elle renvoie un tableau vide lorsque l'application n'est pas activée) et cela fonctionne la plupart du temps, mais parfois, il renvoie 0 résultat même lorsque mon application est activée.


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
}

Existe-t-il un moyen de vérifier si l’utilisation est activée pour mon application?

21
Lior Iluz

A reçu une excellente réponse de quelqu'un sur Twitter, a testé le fonctionnement:

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;
}
26
Lior Iluz

J’avais précédemment utilisé le même code que Bao Le, mais j’ai rencontré le problème suivant: certains périphériques (par exemple, le VF-895N) signalent que les statistiques d’utilisation sont activées même s’ils ne le sont pas. Pour contourner le problème, j'ai modifié mon code comme suit:

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

Testé avec succès sur plusieurs appareils.

4
Markus

Ceci est une solution alternative:

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;
2
Bao Le

Cela fonctionne jusqu'à 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;
0
Tyler

Détecter lorsque l'accès à l'utilisation change

Utilisez cette classe pour être averti lorsque votre accès est autorisé ou révoqué.

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

}

Crédits

Basé sur le code de epicality in autre réponse .

0
Sam