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

后端 未结 5 565
执笔经年
执笔经年 2020-11-30 01:24

I\'ve been getting reports of some Xiaomi devices (e.g. Mi 2, running API level 21) not showing overlays. My app targets API 23.

There are several posts out there r

相关标签:
5条回答
  • 2020-11-30 01:52

    1) on pre-API 23, the permission is already given, because the user granted it upon install.

    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. No idea how Google fixed it though.

    2) This way:

    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:

    @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. 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. 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.

    0 讨论(0)
  • 2020-11-30 01:59

    You can check permission like this:

    boolean granted = activity.checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW") == PackageManager.PERMISSION_GRANTED;
    
    0 讨论(0)
  • 2020-11-30 02:02

    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

    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

    0 讨论(0)
  • 2020-11-30 02:04

    Checking if you have the drawOverlays permission is safer using this:

    @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. 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);
    
    }
    
    0 讨论(0)
  • 2020-11-30 02:14

    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.

    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.

    0 讨论(0)
提交回复
热议问题