Подтвердить что ты не робот

Как обращаться с полномочиями SYSTEM_ALERT_WINDOW, которые не авто-предоставляются на некоторых устройствах с предварительным указателем Marshmallow

Я получаю сообщения о некоторых устройствах Xiaomi (например, Mi 2, уровень API API 21), не отображающий наложения. Мое приложение предназначено для API 23.

Есть несколько сообщения об этом. Похоже, что устройства MIUI не разрешают это разрешение во время установки (в отличие от других устройств с предварительным указателем Marshmallow).

К сожалению, Settings.canDrawOverlays() работает только на Android 23 +.

  • Каков правильный способ проверить, не разрешено ли это разрешение pre-Marshmallow?
  • Есть ли намерение взять пользователя на соответствующую страницу настроек MUIU? Может быть: new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName), но у меня нет средств для проверки этого.
4b9b3361

Ответ 1

1) на pre-API 23, разрешение уже указано, поскольку пользователь предоставил его при установке.

EDIT: кажется, что на Android 6 есть ошибка (которая будет получить исправлено в 6.0.1), что если пользователь отказал в этом разрешении, приложение выйдет из строя с помощью SecurityException. Не знаю, как Google исправил это, хотя.

2) Таким образом:

public static void requestSystemAlertPermission(Activity context, Fragment fragment, int requestCode) {
    if (VERSION.SDK_INT < VERSION_CODES.M)
        return;
    final String packageName = context == null ? fragment.getActivity().getPackageName() : context.getPackageName();
    final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName));
    if (fragment != null)
        fragment.startActivityForResult(intent, requestCode);
    else
        context.startActivityForResult(intent, requestCode);
}

Затем в onActivityResult вы можете проверить, предоставлено ли разрешение или нет, как таковое:

@TargetApi(VERSION_CODES.M)
public static boolean isSystemAlertPermissionGranted(Context context) {
    final boolean result = VERSION.SDK_INT < VERSION_CODES.M || Settings.canDrawOverlays(context);
    return result;
}

EDIT: пока вы публикуете приложение в Play Маркете, ваше приложение будет автоматически предоставлено с этим разрешением. Вы можете прочитать об этом здесь. Когда я спросил об этом, я подумал, что это часть Android сама по себе, поскольку я думал, что все, что нам нужно, - нацелить достаточно высокое значение для targetSdkVersion. То, что Google написал мне ( здесь), заключается в том, что они хотели избежать проблем с популярными приложениями.

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

Ответ 2

Проверка наличия разрешения drawOverlays безопаснее с помощью этого:

@SuppressLint("NewApi")
public static boolean canDrawOverlayViews(Context con){
    if(Build.VERSION.SDK_INT< Build.VERSION_CODES.LOLLIPOP){return true;}
    try { 
        return Settings.canDrawOverlays(con); 
    }
    catch(NoSuchMethodError e){ 
        return canDrawOverlaysUsingReflection(con); 
    }
}


public static boolean canDrawOverlaysUsingReflection(Context context) {

    try {

        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        Class clazz = AppOpsManager.class;
        Method dispatchMethod = clazz.getMethod("checkOp", new Class[] { int.class, int.class, String.class });
        //AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
        int mode = (Integer) dispatchMethod.invoke(manager, new Object[] { 24, Binder.getCallingUid(), context.getApplicationContext().getPackageName() });

        return AppOpsManager.MODE_ALLOWED == mode;

    } catch (Exception e) {  return false;  }

}

Пользовательские ПЗУ могут изменить ОС так, чтобы это Settings.canDrawOverlays() не было доступно. Это случилось со мной с устройствами Xiaomi, и приложение разбилось.

Запрос разрешения:

@SuppressLint("InlinedApi")
public static void requestOverlayDrawPermission(Activity act, int requestCode){
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + act.getPackageName()));
    act.startActivityForResult(intent, requestCode);

}

Ответ 3

Вот как это сделать: шаг за шагом:

Сначала дайте ниже разрешение в файле манифеста:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Или

<uses-permission-sdk-23 android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Затем обработайте остальную часть вещей, используя следующий код:

 public final static int REQUEST_CODE = 65635;

    public void checkDrawOverlayPermission() {
        /** check if we already  have permission to draw over other apps */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                /** if not construct intent to request permission */
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                /** request permission via start activity for result */
                startActivityForResult(intent, REQUEST_CODE);
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode,  Intent data) {
        /** check if received result code
         is equal our requested code for draw permission  */
        if (requestCode == REQUEST_CODE) {
            // ** if so check once again if we have permission */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (Settings.canDrawOverlays(this)) {
                    // continue here - permission was granted
                    goYourActivity();
                }
            }
        }
    }

Просто вызовите checkDrawOverlayPermission() из своей учетной записи LauncherActivity или где угодно, как ваши требования.

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

Ответ 4

Для поиска в то время как проблема в Xiaomi, Meizu я нашел это. Он отлично работает.

public static boolean isMiuiFloatWindowOpAllowed(@NonNull Context context) {
    final int version = Build.VERSION.SDK_INT;

    if (version >= 19) {
        return checkOp(context, OP_SYSTEM_ALERT_WINDOW); //See AppOpsManager.OP_SYSTEM_ALERT_WINDOW=24 /*@hide/
    } else {
        return (context.getApplicationInfo().flags & 1<<27) == 1;
    }
}

public static boolean checkOp(Context context, int op, String packageName, int uid) {
    final int version = Build.VERSION.SDK_INT;

    if (version >= 19) {
        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        try {
            return (AppOpsManager.MODE_ALLOWED == (Integer) ReflectUtils.invokeMethod(manager, "checkOp", op, uid, packageName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        Flog.e("Below API 19 cannot invoke!");
    }
    return false;
}

ReflectUtils.java

public static Object invokeMethod(@NonNull Object receiver, String methodName, Object... methodArgs) throws Exception {
Class<?>[] argsClass = null;
    if (methodArgs != null && methodArgs.length != 0) {
        int length = methodArgs.length;
        argsClass = new Class[length];
        for (int i=0; i<length; i++) {
            argsClass[i] = getBaseTypeClass(methodArgs[i].getClass());
        }
    }

    Method method = receiver.getClass().getMethod(methodName, argsClass);
    return method.invoke(receiver, methodArgs);
}

Ссылка