简体   繁体   English

如何处理SYSTEM_ALERT_WINDOW权限未在某些Marshmallow设备上自动授予

[英]How to handle SYSTEM_ALERT_WINDOW permission not being auto-granted on some pre-Marshmallow devices

I've been getting reports of some Xiaomi devices (eg Mi 2, running API level 21) not showing overlays. 我一直在收到一些小米设备(例如Mi 2,运行API级别21)没有显示叠加层的报告。 My app targets API 23. 我的应用针对API 23。

There are several posts out there regarding this. 关于此,有几个 帖子 It seems that MIUI devices do not enable this permission at install time (unlike other pre-Marshmallow devices). 似乎MIUI设备在安装时不启用此权限(与其他Marshmallow之前的设备不同)。

Unfortunately, Settings.canDrawOverlays() only works on Android 23+. 不幸的是, Settings.canDrawOverlays()仅适用于Android 23+。

  1. What is the correct way to check whether this permission has not yet been enabled pre-Marshmallow? 在Marshmallow之前检查此权限是否尚未启用的正确方法是什么?
  2. Is there an Intent to take the user to the relevant MUIU settings page? 是否有意图将用户带到相关的MUIU设置页面? Maybe: new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName) but I have no means to test this. 也许: new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", packageName)但我无法测试这个。

1) on pre-API 23, the permission is already given, because the user granted it upon install. 1)在API 23之前,已经给出了权限,因为用户在安装时授予了权限。

EDIT: it seems there is a bug on Android 6 (that will get fixed on 6.0.1 ), that if the user has denied this permission , the app will crash with SecurityException. 编辑:似乎Android 6上有一个错误(将在6.0.1上修复 ),如果用户拒绝此权限,应用程序将与SecurityException崩溃。 No idea how Google fixed it though. 不知道谷歌如何修复它。

2) This way: 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);
}

Then, in the onActivityResult, you can check if the permission is given or not, as such: 然后,在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: for the time being, if you publish an app to the Play Store, your app will be auto-granted with this permission. 编辑:目前,如果您将应用程序发布到Play商店,您的应用程序将自动获得此权限。 You can read about it here . 你可以在这里阅读它。 When I asked about it, I thought it was a part of Android itself, as I thought all we need is to target a high enough value for targetSdkVersion. 当我询问它时,我认为它是Android本身的一部分,因为我认为我们所需要的是为targetSdkVersion设定足够高的值。 What Google wrote to me ( here ) is that they wanted to avoid issues on popular apps. 谷歌写给我的( 这里 )是他们希望避免流行应用上的问题。

I suggest handling this permission correctly even if you will get it auto-granted. 我建议正确处理此权限,即使您将自动授予它。

Checking if you have the drawOverlays permission is safer using this: 使用以下方法检查您是否具有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;  }

}

Custom ROMs can have altered the OS so that that Settings.canDrawOverlays() is not available. 自定义ROM可以更改操作系统,以便Settings.canDrawOverlays()不可用。 This happened to me with Xiaomi devices and the app crashed. 小米设备和应用程序崩溃后发生了这种情况。

Requesting the permission: 申请许可:

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

}

Here is the step by step how to handle this: 这是一步一步如何处理这个:

At first give below permission in manifest file: 首先在清单文件中给出以下权限:

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

Or 要么

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

Then handle rest of the things using below code: 然后使用以下代码处理其余的事情:

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

Just call checkDrawOverlayPermission() from your LauncherActivity or anywhere as your requirements. 只需从LauncherActivity或任何地方调用checkDrawOverlayPermission()

When you execute the project you will see a window and asked for enabling the permission. 执行项目时,您将看到一个窗口并要求启用该权限。 After allowing permission you will be able to do anything about this. 在获得许可后,您将能够对此做任何事情。

For searching for while about the issue in Xiaomi, Meizu I have found this. 为了寻找关于小米的问题,魅族我找到了这个。 It's working perfectly.. 它完美地工作..

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

Reference 参考

你可以检查这样的权限:

boolean granted = activity.checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW") == PackageManager.PERMISSION_GRANTED;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM