Android 5.1.1 и выше - getRunningAppProcesses() возвращает только мой пакет приложений

Похоже, Google наконец закрыл все двери для получения текущего пакета приложений переднего плана.

После обновления Lollipop, которое убило getRunningTasks(int maxNum) и благодаря этому ответу, я использовал этот код для получения пакета приложения переднего плана начиная с Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Android 5.1.1 и выше (6.0 Marshmallow), похоже, убили и getRunningAppProcesses(). Теперь он возвращает список вашего собственного пакета приложения.


UsageStatsManager

Мы можем использовать новый API UsageStatsManager как описано здесь, но он работает не для всех приложений. Некоторые системные приложения возвращают один и тот же пакет

com.google.android.googlequicksearchbox

AccessibilityService (декабрь 2017: запрет на использование Google)

Некоторые приложения используют AccessibilityService (как показано здесь), но у него есть некоторые недостатки.


Есть ли другой способ получить текущий запущенный пакет приложения?

Ответ 1

Чтобы получить список запущенных процессов на Android 1.6 - Android 6.0, вы можете использовать эту библиотеку, которую я написал: https://github.com/jaredrummler/AndroidProcesses Библиотека читает /proc для получения информации о процессе.

Google значительно ограничил доступ к /proc в Android Nougat. Чтобы получить список запущенных процессов на Android Nougat, вам нужно будет использовать UsageStatsManager или получить доступ root.

Щелкните историю изменений для предыдущих альтернативных решений.

Ответ 2

 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

Используйте этот метод для получения задачи переднего плана. U потребуется системное разрешение "android: get_usage_stats"

public static boolean needPermissionForBlocking(Context context){
    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 true;
    }
}

ЕСЛИ пользователь включит это в настройках → Безопасность- > приложение с доступом к использованию. После этого вы получите переднюю задачу. Подобный процесс Clean matser от Cheetahamobile ссылка для google

Ответ 3

Взгляните на https://github.com/ricvalerio/foregroundappchecker, это может быть то, что вам нужно. Предоставляет пример кода и устраняет боль при использовании переднего плана переднего плана.

Вот два примера:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Или регулярно проверяйте:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);

Ответ 4

Google ограничил эту функциональность только для системных приложений. Как сообщается в ошибке ошибки, вам потребуется разрешение REAL_GET_TASKS для доступа туда.

Теперь приложения должны иметь... permission.REAL_GET_TASKS, чтобы иметь возможность получить информацию о процессе для всех приложений. Только процесс информация для вызывающего приложения будет возвращена, если приложение не имеет разрешения. Привилегии приложений будут временно доступны для получения информации о процессе для всех приложений, если у них нет новое разрешение, но устарело... permission.GET_TASKS Кроме того, только системные приложения могут получить разрешение REAL_GET_TASKS.

Ответ 5

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

Это

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    long time = System.currentTimeMillis();
    List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
    if (appList != null && appList.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : appList) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }
}

Может быть упрощено к этому

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

Я обнаружил, что использую этот код в 2-секундном цикле и задаюсь вопросом, почему я использовал сложное решение, которое было O (n * log (n)), когда в Collections.max() было доступно более простое решение, которое О (п).

Ответ 6

public class AccessibilityDetectingService extends AccessibilityService {

@Override
protected void onServiceConnected() {
    super.onServiceConnected();

    //Configure these here for compatibility with API 13 and below.

    AccessibilityServiceInfo config = new AccessibilityServiceInfo();
    config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

    if (Build.VERSION.SDK_INT >= 16)
        //Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

    setServiceInfo(config);
}

@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Затем запустите доступ к сервису и приложениям на своем устройстве Настройка- > accessibility- > App на этой службе.

Ответ 7

//Это работает быстрее, чем ответ выше.

 String mforegoundPppPackageName;
 UsageStatsManager usage = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
 long time = System.currentTimeMillis();

 List<UsageStats> stats = usage.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, time);
 Collections.sort(stats, usageStatsComparator);

      if (stats != null && stats.size() > 0) {
          if (stats.get(stats.size()).getPackageName().equals("android")) {
              stats.remove(stats.size());
          }
          mforegoundPppPackageName = stats.get(stats.size()).getPackageName();
      } else {
          mforegoundPppPackageName = "";
      }

**//How to sort**

Comparator<UsageStats> usageStatsComparator = new Comparator<UsageStats>() {
        @Override
        public int compare(UsageStats o1, UsageStats o2) {
            if (o1.getLastTimeUsed() > o2.getLastTimeUsed()) {
                return 1;
            } else if (o1.getLastTimeUsed() < o2.getLastTimeUsed()) {
                return -1;
            } else {
                return 0;
            }
        }
    };

Ответ 8

Попробуйте использовать getRunningServices() вместо метода getRunningAppProcesses().

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);