GetRunningTasks не работает в Android L

В Android L у Google отключена функция getRunningTasks. Теперь он может только вернуть собственную задачу приложений и домашнюю пусковую установку. Я больше не могу запускать другие приложения. Наше приложение нуждается в этом методе для определения текущего верхнего приложения. У кого-нибудь есть другой способ сделать это?

Я искал в Google, больше никаких тем об этом кроме этого: https://code.google.com/p/android-developer-preview/issues/detail?id=29

Ответ 1

Для недавнего проекта, над которым я работал, мне также необходимо определить, когда запускаются определенные приложения. Все мои исследования приводят к методу getRunningTasks, который устарел, начиная с Lollipop. Однако, к моим неожиданностям, я обнаружил, что некоторые приложения приложения блокировки по-прежнему работают на устройствах с леденец, поэтому они должны придумать решение, чтобы обойти это. Так что я вырыл немного глубже. Вот что я узнал:

    • На устройствах pre-L они все еще используют getRunningTasks
    1. На устройствах L они используют getRunningAppProcesses, который возвращает список процессов, выполняемых в настоящее время на устройствах. Вы можете подумать "ну, это не полезно". Каждый processInfo имеет приписываемое значение. Когда приложение становится главной активностью, его значение processInfo также изменяется на IMPORTANCE_FOREGROUND. Таким образом, вы можете отфильтровать те процессы, которые не находятся на переднем плане. Из каждого ProcessInfo вы также можете запросить список загруженных пакетов. Затем вы можете проверить, содержит ли список тот же пакет, что и приложение при попытке "защитить".

Некоторые примеры кода для обнаружения при запуске приложения по умолчанию для календаря:

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

Примечание. getRunningAppProcesses также предназначен для отладки или "создания пользовательского интерфейса управления процессом, ориентированного на пользователя". Не уверен, что Google закроет этот бэкдор аналогичным образом, чтобы сделать getRunningTasks.

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

Ответ 2

Как упоминалось MKY, метод getRunningTasks() не работает для получения текущего приложения в Lollipop.

Как писал sunxin8086, одним из способов получения запущенных приложений является использование метода getRunningAppsProcesses(). Однако условие info.importance == IMPORTANCE_FOREGROUND не может определить текущее приложение уникально.

Лучшим подходом для определения текущего приложения переднего плана может быть проверка поля processState в объекте RunningAppProcessInfo. Это поле является скрытым полем, но вы можете увидеть его в классе RunningAppProcessInfo. Если это значение ActivityManager.PROCESS_STATE_TOP (которое также скрытая статическая константа), этот процесс является текущим процессом переднего плана.

Например, код

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;

Примечание. Поле processState не существует в pre-Lolipop. Перед запуском вышеуказанного кода проверьте Build.VERSION.SDK_INT >= 21. Вышеприведенный код работает только для Lollipop +.

Другой подход, сделанный Гастоном (это совсем другое), и значение "текущего приложения" несколько отличается от этого подхода.

Выберите один для своей цели.


[EDIT]

Как заметил Сэм, я изменил START_TASK_TO_FRONT на PROCESS_STATE_TOP. (Оба значения равны 2)

[EDIT2]

У Сэма новая находка! Чтобы однозначно определить приложение переднего плана, еще один условие

process.importanceReasonCode == 0

. Вышеупомянутый код обновлен. Спасибо!

Ответ 3

Здесь вы найдете точное решение для получения текущей активности на устройствах Android L/Lollipop и устройствах Android M/Marshmallow.

Сначала вызовите эту строку кода: (Один раз)

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

В приведенном выше коде откроется экран с названием "Приложения с доступом к использованию". Просто установите переключатель в положение on/true, чтобы разрешить использование доступа. Теперь вызовите следующий метод в своей службе или в любом месте:

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

добавить разрешение

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

Это вернет имя пакета текущей активности, будь то facebook или whatsapp.

Единственное усложнение этого метода - вы должны попросить пользователя разрешить статистику использования приложения... т.е. первый шаг.

Надежда! это помогает каждому.

Ответ 4

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

Я думаю, что невозможно получить другие задачи приложения,

Вот что говорит документация

С введением новых параллельных документов и мероприятий функции задач в предстоящем выпуске (см. Параллельные документы и в разделе "Реценты" ниже), Метод ActivityManager.getRecentTasks() теперь устарел для улучшения конфиденциальность пользователей. Для обратной совместимости этот метод все еще возвращает небольшое подмножество его данных, включая собственные вызывающие приложения задачи и, возможно, некоторые другие не чувствительные задачи (например, Home). Если ваше приложение использует этот метод для извлечения собственных задач, использования android.app.ActivityManager.getAppTasks() вместо этого, чтобы получить это информация.

Ознакомьтесь с обзором api для Android L здесь https://developer.android.com/preview/api-overview.html#Behaviors