web-dev-qa-db-fra.com

getRunningTasks ne fonctionne pas dans Android L

Dans Android L, Google a désactivé getRunningTasks. Il ne peut désormais renvoyer que la tâche de ses propres applications et le programme de lancement personnel. Je ne peux plus obtenir d'autres tâches d'applications. Notre application a besoin de cette méthode pour déterminer la meilleure application actuelle. Quelqu'un a une autre méthode pour le faire?

J'ai cherché dans Google, plus de sujets à ce sujet, sauf ceci: https://code.google.com/p/Android-developer-preview/issues/detail?id=29

50
Ligen Yao

Pour un projet récent sur lequel j'ai travaillé, je dois également détecter le lancement de certaines applications. Toutes mes recherches ont abouti à la méthode getRunningTasks, déconseillée à partir de Lollipop. Cependant, à ma grande surprise, j'ai découvert que certaines des applications de verrouillage d'applications fonctionnaient toujours sur les appareils Lollipop. Ils ont donc dû proposer une solution pour résoudre ce problème. J'ai donc creusé un peu plus profondément. Voici ce que j'ai découvert:

    1. Sur les périphériques pré-L, ils utilisent toujours getRunningTasks
    1. Sur les périphériques L, ils utilisent getRunningAppProcesses, qui renvoie la liste des processus en cours d'exécution sur les périphériques. Vous pourriez penser "bien, ce n'est pas utile". Chaque processInfo a une importance appelée attribut. Lorsqu'une application devient la première activité, son importance processInfo devient également IMPORTANCE_FOREGROUND. Vous pouvez donc filtrer les processus qui ne sont pas au premier plan. A partir de chaque ProcessInfo, vous pouvez également demander une liste des packages chargés. Vous pouvez ensuite vérifier si la liste contient le même package que celui utilisé par l'application lorsque vous essayez "protégé".

Quelques exemples de code à détecter lors du lancement de l'application de calendrier par défaut:

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

Remarque: getRunningAppProcesses est également conçu pour le débogage ou la "création d'une interface utilisateur de gestion de processus". Pas sûr que Google ferme cette porte dérobée de la même manière que pour getRunningTasks.

Donc non, vous ne pouvez plus obtenir le topActivity. Mais avec un peu de piratage, vous pouvez obtenir un résultat similaire.

45
sunxin8086

Comme MKY l'a mentionné, la méthode getRunningTasks() ne fonctionne pas pour obtenir l'application actuelle dans Lollipop.

Comme l'a écrit sunxin8086, l'un des moyens d'obtenir les applications en cours consiste à utiliser la méthode getRunningAppsProcesses(). Cependant, la condition info.importance == IMPORTANCE_FOREGROUND ne peut pas déterminer l'application actuelle uniquement.

La meilleure approche pour déterminer l’application de premier plan actuelle peut consister à vérifier le champ processState dans l’objet RunningAppProcessInfo. Ce champ est un champ masqué, mais vous pouvez le voir dans la classe RunningAppProcessInfo. Si cette valeur est ActivityManager.PROCESS_STATE_TOP (qui est également une constante statique masquée), le processus est le processus de premier plan actuel.

Par exemple, le code est

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;

Remarque: Le champ processState n'existe pas dans les pré-Lolipop. Veuillez vérifier que Build.VERSION.SDK_INT >= 21 avant d'exécuter le code ci-dessus. Le code ci-dessus ne fonctionne que pour Lollipop +.

L'autre approche, de Gaston (qui est assez différente), et la signification de "application actuelle" est légèrement différente de cette approche.

Veuillez en choisir un pour votre objectif.


[MODIFIER]

Comme Sam l'a souligné, j'ai modifié START_TASK_TO_FRONT par PROCESS_STATE_TOP. (Les deux valeurs sont 2)

[EDIT2]

Sam a une nouvelle trouvaille! Pour déterminer l'application de premier plan de manière unique, une autre condition

process.importanceReasonCode == 0

est nécessaire. Le code ci-dessus a été mis à jour. Merci!

33
KNaito

Voici une solution exacte pour obtenir l’activité maximale actuelle sur vos appareils Android L/Lollipop et Android M/Marshmallow.

Appelle d'abord cette ligne de code: (Une fois)

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

Le code ci-dessus ouvrira un écran intitulé "Applications avec accès utilisateur". Il suffit de cocher le bouton radio pour activer/true pour autoriser l'accès à l'utilisation. Appelez maintenant la méthode suivante dans votre service ou n'importe où:

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

ajouter la permission

<uses-permission Android:name="Android.permission.PACKAGE_USAGE_STATS"
     tools:ignore="ProtectedPermissions" />

Cela renverra le nom du paquet d'activité en cours, qu'il s'agisse de facebook ou de whatsapp.

La seule complication de cette méthode est que vous devez Inviter l'utilisateur à autoriser les statistiques d'utilisation de l'application ... c'est-à-dire la première étape.

Espérer! cela aide tout le monde.

28
Udit Kapahi
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"/>
5
user4759293

Je pense que ce n'est pas possible d'obtenir les tâches d'autres applications,

C'est ce que dit la documentation

Avec l’introduction des nouvelles tâches simultanées de documents et d’activités dans la prochaine version (voir Les documents et activités simultanés dans l’écran Récents ci-dessous ), la méthode ActivityManager.getRecentTasks () est maintenant obsolète pour améliorer la confidentialité des utilisateurs. Pour des raisons de compatibilité ascendante, cette méthode renvoie toujours un petit sous-ensemble de ses données, y compris les tâches propres à l’appel appelant et éventuellement d’autres tâches non sensibles (telles que Home). Si votre application utilise cette méthode pour récupérer ses propres tâches, utilisez plutôt Android.app.ActivityManager.getAppTasks () pour récupérer ces informations.

Découvrez la vue d'ensemble de l'API de Android L ici https://developer.Android.com/preview/api-overview.html#Behaviors

3
Mohan Krishna