美文网首页
Android权限适配(二)

Android权限适配(二)

作者: Parallel_Lines | 来源:发表于2019-01-14 20:38 被阅读0次

本文接 Android权限适配(一)

悬浮窗权限

悬浮窗权限同样属于上文中说到的特殊权限。

悬浮窗代码的设置

要使用下文中悬浮窗权限的判别和申请代码,悬浮窗需要如下设置。

  1. AndroidManifest配置权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  2. WindowManager.LayoutParams.type在8.0上有所不同,需要如下配置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}

WindowManager.LayoutParams.TYPE_TOAST只在部分少量机型可用,尽可能少用。

判断是否有悬浮窗权限

对于4.3及以下系统,悬浮窗权限均为true。

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
    return true;
} 

4.4~6.0以下系统,可以通过反射AppOpsManager获取悬浮窗权限(AppOpsManager是API 19新增的类)。

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    try {
        Class cls = Class.forName("android.content.Context");
        Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE");
        declaredField.setAccessible(true);
        Object obj = declaredField.get(cls);
        if (!(obj instanceof String)) {
            return false;
        }
        String str2 = (String) obj;
        obj = cls.getMethod("getSystemService", String.class).invoke(context, str2);
        cls = Class.forName("android.app.AppOpsManager");
        Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED");
        declaredField2.setAccessible(true);
        Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
        int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName());
        return result == declaredField2.getInt(cls);
    } catch (Exception e) {
        return false;
    }
}

6.0 Android系统提供了对应的方法获取悬浮窗权限。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    return Settings.canDrawOverlays(context);
}

实测Settings.canDrawOverlays(context)兼容至Android最新版(当前9.0)。

跳转开启悬浮窗权限的页面

Android在6.0及以上同样提供了系统级的悬浮窗权限开启页面。如下跳转:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    try {
        Class clazz = Settings.class;
        Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
        Intent intent = new Intent(field.get(null).toString());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setData(Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

但是在6.0之下,悬浮窗权限开启页面真是金屋藏娇,该页面不仅区别于不同rom,甚至区别于相同rom的不同版本,更有甚者将其藏在了手机管家App里,真是找的辛苦。

考虑到现今4.x、5.x手机占比不高且只会越来越少,这里只提供前往app设置页面的代码。如果各位有需要可以根据手机rom自行适配。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.fromParts("package", context.getPackageName(), null));
    context.startActivity(intent);
}

4.4以下版本建议忽略。

写在最后

Google起初对悬浮窗抱有很好的愿景。

2.x时期,Google并没有把悬浮窗作为一个权限,它以为大家都会按需使用悬浮窗功能,自觉维护Android环境。然而没料想到开发者们把悬浮窗用开了花,毕竟谁的App都想时刻占据用户流量。这次,开发者坑了爹。

4.x、5.x时期,手机厂商们急于解决这种问题,将悬浮窗默认关停,只留下一个鬼都找不到的悬浮窗权限开启页面。在没有Google参与的情况下,根本没有Android API可以判断、开启悬浮窗权限。那些本不需要悬浮窗权限的App毫无影响,依赖悬浮窗生存的App则懵了逼,只能不停的兼容、兼容。这次,手机厂商坑了爹。

6.x之后,Google爸爸终于解决了这个问题:他将悬浮窗"纳入权限",然后提供官方的api供判断是否有悬浮窗权限,最后提供官方的页面供开启悬浮窗权限。

历史遗留问题终会随时间解决,经验教训难得可贵:大环境的维护永远需要最底线的规则,光靠自觉是行不通的。

无障碍辅助权限

利用无障碍辅助可以实现诸如自动安装、抢红包等功能。

该权限没有太多的适配点。

判断是否有无障碍辅助权限

    public static boolean isSettingsOn(Context mContext, Class serviceClass) {
        int accessibilityEnabled = 0;
        final String service = mContext.getPackageName() + "/" + serviceClass.getCanonicalName();

        try {
            accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),
                    android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
        if (accessibilityEnabled == 1) {
            String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                mStringColonSplitter.setString(settingValue);
                while (mStringColonSplitter.hasNext()) {
                    String accessibilityService = mStringColonSplitter.next();
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

其中class为继承自AccessibilityService的无障碍功能类。
上述代码兼容至Android最新版本(9.x)。

跳转至无障碍辅助页面

通常情况下下面代码会跳转至需要无障碍辅助的应用列表页,但是不排除个别rom修改了无障碍辅助的页面层级关系,这种情况如有需要应单独针对rom做适配。

    public static void jumpToSetting(final Context context) {
        try {
            Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        } catch (Throwable e) {//出现异常 可能和rom删除了该页面有关
            e.printStackTrace();
        }
    }

相关文章

网友评论

      本文标题:Android权限适配(二)

      本文链接:https://www.haomeiwen.com/subject/cfxsdqtx.html