本文接 Android权限适配(一)
悬浮窗权限
悬浮窗权限同样属于上文中说到的特殊权限。
悬浮窗代码的设置
要使用下文中悬浮窗权限的判别和申请代码,悬浮窗需要如下设置。
- AndroidManifest配置权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
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();
}
}
网友评论