MIUI 判断悬浮窗权限

/**
* Created by chenzy on 2015/3/31.
*
* MIUI 悬浮窗判断工具类
*/
public class AlterWindowUtil {
    public static final String TAG ="AlterWindowUtil";

    /**
     * 4.4 以上可以直接判断准确
     *
     * 4.4 以下非MIUI直接返回true
     *
     * 4.4 以下MIUI 可 判断 上一次打开app 时 是否开启了悬浮窗权限
     *
     * @param context
     * @return
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean isFloatWindowOpAllowed(Context context) {
        final int version = Build.VERSION.SDK_INT;

        if(!DeviceUtil.isFlyme4() && !DeviceUtil.isMiui(context)){
            return true;
        }

        if (version >= 19) {
            return checkOp(context, 24);  //自己写就是24 为什么是24?看AppOpsManager //AppOpsManager.OP_SYSTEM_ALERT_WINDOW
        } else {
            if(DeviceUtil.isMiui(context)){
                if ((context.getApplicationInfo().flags & 1 << 27) == 1 <<27 ) {
                    return true;
                } else {
                    return false;
                }
            }else{
                return true;
            }
        }

    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean checkOp(Context context, int op) {
        final int version = Build.VERSION.SDK_INT;

        if (version >= 19) {
            AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            try {
                Class managerClass = manager.getClass();
                Method method = managerClass.getDeclaredMethod("checkOp", int.class, int.class, String.class);
                int isAllowNum = (Integer) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());

                if (AppOpsManager.MODE_ALLOWED == isAllowNum) {
                    return true;
                } else {
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

}

<think>我们正在解决如何判断Android设备上的悬浮窗权限是否已开启的问题。根据引用内容,我们知道悬浮窗权限(SYSTEM_ALERT_WINDOW)在Android M(6.0)之后需要动态申请,并且在不同系统版本中授权方式可能不同。在Android 14.0中,授权方式是根据包名进行授权。 关键点: 1. 在Android M(6.0)及以上版本,需要检查悬浮窗权限。 2. 检查悬浮窗权限的方法:使用`Settings.canDrawOverlays(context)`方法(API 23及以上)。 3. 对于低于API 23(Android M)的系统,默认有悬浮窗权限,因为权限是在安装时授予的。 因此,我们可以编写一个工具方法来判断悬浮窗权限是否开启: 步骤: 1. 如果设备运行的Android版本低于M(API 23),则直接返回true,因为不需要动态申请。 2. 对于API 23及以上,使用`Settings.canDrawOverlays(context)`来判断。 注意:在Android 14.0中,授权方式有所变化,但`Settings.canDrawOverlays`方法仍然适用,因为它内部会处理不同版本的逻辑。 代码示例: ```java public static boolean hasOverlayPermission(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return Settings.canDrawOverlays(context); } else { // 在API 23以下,默认有权限(除非系统有特殊处理,但一般厂商不会在低版本上限制) return true; } } ``` 但是,需要注意的是,有些厂商在低版本Android上也可能修改了权限管理,因此不能完全保证在低于M的设备上一定有权限。但根据Android官方设计,在M之前,悬浮窗权限是默认开启的,且没有提供关闭的选项。所以,我们通常这样处理。 另外,根据引用[3]提到,在Android 6.0上,悬浮窗权限被禁用,需要用户手动开启,且开启路径较深。因此,在Android 6.0及以上,我们必须检查该权限。 所以,我们使用上述方法即可。 但是,还有一种情况:在部分国产ROM(如小米、华为等)上,即使使用`Settings.canDrawOverlays`方法,也可能因为权限管理不同而返回错误结果。因此,对于这些设备,可能需要额外的检查。不过,官方推荐的方法是使用`Settings.canDrawOverlays`,因为它会调用标准的API。对于国产ROM,它们通常也会兼容这个方法,因为它们在设置中提供了授权界面,并且会修改这个方法的返回值以反映授权状态。 因此,我们可以使用上述方法作为标准方法。 另外,根据引用[2]提到,在Android 14.0中,系统根据包名授权悬浮窗权限。我们的方法中传入的context包含了包名,所以`Settings.canDrawOverlays`会检查当前应用的包名是否被授权。 总结:使用`Settings.canDrawOverlays`是最标准的方法。 使用示例: 在Activity或Fragment中: ```java if (hasOverlayPermission(this)) { // 已经开启悬浮窗权限 showFloatingWindow(); } else { // 没有开启,需要申请 requestOverlayPermission(); } ``` 申请悬浮窗权限的代码(在Activity中): ```java private static final int REQUEST_OVERLAY_PERMISSION = 1001; private void requestOverlayPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION); } } ``` 然后在`onActivityResult`中处理结果: ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_OVERLAY_PERMISSION) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Settings.canDrawOverlays(this)) { // 用户授予权限 showFloatingWindow(); } else { // 用户拒绝权限 Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show(); } } } } ``` 注意:在Android 6.0及以上,申请悬浮窗权限不是通过`ActivityCompat.requestPermissions`,而是通过启动系统设置界面让用户手动开启。 因此,判断悬浮窗权限是否开启,我们主要使用`Settings.canDrawOverlays`方法。 引用说明: - 在Android M(6.0)及以上,需要动态申请悬浮窗权限[^3]。 - 在Android 14.0中,授权方式是根据包名进行授权,但使用`Settings.canDrawOverlays`方法仍然适用[^2]。 所以,我们按照上述方式实现即可。</think>在 Android 开发中判断悬浮窗权限(`SYSTEM_ALERT_WINDOW`)是否已开启,可以通过以下方法实现: ### 判断悬浮窗权限的核心方法 ```java // 检查悬浮窗权限是否开启 public static boolean hasOverlayPermission(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Android 6.0+ 使用标准API检查 return Settings.canDrawOverlays(context); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4-5.1 的特殊处理 return checkOp(context, AppOpsManager.OP_SYSTEM_ALERT_WINDOW); } else { // Android 4.3及以下默认有权限 return true; } } // 兼容Android 4.4-5.1的检查方法 @TargetApi(Build.VERSION_CODES.KITKAT) private static boolean checkOp(Context context, int op) { AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); try { Class<?> clazz = AppOpsManager.class; Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); int result = (int) method.invoke(manager, op, android.os.Process.myUid(), context.getPackageName()); return result == AppOpsManager.MODE_ALLOWED; } catch (Exception e) { Log.e("PermissionUtils", "checkOp error", e); } return false; } ``` ### 使用示例 ```java // 在Activity或Fragment中检查权限 if (hasOverlayPermission(this)) { // 已开启悬浮窗权限 showFloatingWindow(); } else { // 未开启权限,引导用户开启 requestOverlayPermission(); } // 请求悬浮窗权限的方法 private void requestOverlayPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION); } else { // 对于低版本Android,引导用户到设置页面 showManualPermissionGuide(); } } // 处理权限请求结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_OVERLAY_PERMISSION) { if (hasOverlayPermission(this)) { // 用户已授权 showFloatingWindow(); } else { // 用户拒绝授权 Toast.makeText(this, "悬浮窗权限被拒绝", Toast.LENGTH_SHORT).show(); } } } ``` ### 关键说明 1. **Android 6.0+ (API 23+)** - 使用标准API `Settings.canDrawOverlays(context)` 检查权限 - 通过 `Settings.ACTION_MANAGE_OVERLAY_PERMISSION` 引导用户开启权限 2. **Android 4.4-5.1 (API 19-22)** - 使用反射调用 `AppOpsManager.checkOp()` 方法 - 检查 `OP_SYSTEM_ALERT_WINDOW` 操作模式是否为 `MODE_ALLOWED` 3. **Android 4.3及以下** - 默认有悬浮窗权限,无需特殊处理 ### 注意事项 1. **权限声明**:在 AndroidManifest.xml 中添加权限声明 ```xml <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> ``` 2. **国产ROM适配**: ```java // 检查小米ROM的特殊权限设置 public static boolean isXiaomiOverlayPermissionEnabled(Context context) { return Settings.Secure.getInt(context.getContentResolver(), "miui_overlay_permission", 0) == 1; } ``` 3. **权限路径差异**: - 不同厂商设备权限路径不同(如小米的"显示悬浮窗",华为的"悬浮窗管理"等) - 需要提供明确的引导提示[^3] 4. **Android 14+ 变更**: - 需要添加 `<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>` - 部分设备需要额外处理[^2] ### 最佳实践建议 1. 在需要显示悬浮窗前检查权限状态 2. 提供友好的用户引导界面 3. 处理权限拒绝后的降级方案 4. 针对不同Android版本和厂商ROM做兼容处理 > 悬浮窗权限检查是Android开发中的常见需求,标准方法是使用`Settings.canDrawOverlays()`[^1],但需要考虑不同Android版本和厂商ROM的兼容性问题[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值