如何在Android Lollipop上检查是否已授予权限SYSTEM_ALERT_WINDOW?

4
请注意,我正在讨论的是Android Lollipop。对于Android 6.0,我们可以使用方法canDrawOverlays()来检查SYSTEM_ALERT_WINDOW是否已被授予。

几乎所有设备在Android Lollipop上默认授予此权限。但在一些小米、魅族等设备上,这个权限可能不会被授予。用户需要进入“应用信息”中允许它。

我们如何通过程序检查它以警告用户呢?

2个回答

3
在MIUI中使用
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);
}

非常感谢!我会尝试的! - TOP
我在哪里可以获取OP_SYSTEM_ALERT_WINDOW?什么是uid? - TOP
我找不到 ReflectUtils。它在哪里可以获取? - TOP
5
你在使用两个参数调用checkOp函数,而在下面定义该函数时却有四个参数。!!!我的意思是...如果你要复制/粘贴,请至少快速查看一下你正在复制/粘贴的内容... - Anonymous
仍然没有答案在哪里获取OP_SYSTEM_ALERT_WINDOW或getBaseTypeClass吗? - Hatzen

2

反射是有风险的,因为你会认为某些东西是确定的……但在未来版本的Android中,这些东西可能会发生改变。以下方法只在正确的方式失败时使用反射。

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

 }

在小米Note3中获取AppOpsManager.MODE_IGNORED或值1。需要进一步的建议。 - UMESH0492
如果你没有权限...就要去请求它:Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + act.getPackageName())); startActivityForResult(intent, requestCode); - Anonymous
返回 android.content.ActivityNotFoundException: 没有找到处理 Intent { act=android.settings.action.MANAGE_OVERLAY_PERMISSION dat=package:com.findmeout.android } 的活动。 - UMESH0492
你用什么设备进行测试?我在任何设备或模拟器上使用它都从未出现过错误,包括小米红米3手机。 - Anonymous
为什么需要反射?checkOp是从API 19开始的,然后你无论如何都返回true... - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接