从Android SDK 23 开始, Android就改变了权限的管理模式。对于一些涉及用户隐私的权限则需要用户的授权才可以使用。在此之前,开发者只需要在AndroidManifest.xml中注册,如网络权限、wifi权限等等:权限即可,但是现在除了注册还需要进行手动的授权。
没有涉及用户隐私的权限申请的方式比较简单,即只需要在AndroidManifest.xml中注册,如网络权限、wifi权限等等:
本文主要研究涉及用户隐私权限的判断和申请方法,根据需要用户的操作不同,将这类权限分为三类:
以申请手机识别码和申请读写手机存储权限为例,具体代码如下:
boolean hasPhoneStatePermission = false;
boolean hasWriteStorePermission = false;
// 判断是否具有某权限的方法ContextCompat.checkSelfPermission()
if (ContextCompat.checkSelfPermission()(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
hasPhoneStatePermission = true;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
hasWriteStorePermission = true;
}
if (hasPhoneStatePermission && hasWriteStorePermission) {
return;
}
// 可一次申请多个权限的方法ActivityCompat.requestPermissions(),会依次弹出对应的权限弹框
ActivityCompat.requestPermissions(this, { Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE);
PS:权限必须在AndroidMainifest.xml里面声明,否则不生效:
首页需要判断是否已具有要申请的权限,不同的权限判断方法不同,需要单独区分权限,本文罗列几个常用权限通知栏读取权限、无障碍权限以及悬浮窗权限的判断方法:
是否具有通知栏读取权限判断方法:
public static boolean isNoticationEnabled(String packageName) {
String allowedInfo = Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
if (allowedInfo == null || !allowedInfo.contains(packageName)) {
return false;
} else {
return true;
}
}
是否具有无障碍权限判断方法:
public static boolean isAccessibilityServiceEnabled(String packageName) {
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
List accessibilityServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo info : accessibilityServices) {
if (info != null && info.getId().contains(packageName)) {
Toast.makeText(this, "恭喜你,你的应用已具有无障碍权限!!!", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
是否具有悬浮窗权限判断方法:
public static boolean checkAlertWindowsPermission(Context context) {
try {
Object object = context.getSystemService(Context.APP_OPS_SERVICE);
if (object == null) {
return false;
}
Class localClass = object.getClass();
Class[] arrayOfClass = new Class[3];
arrayOfClass[0] = Integer.TYPE;
arrayOfClass[1] = Integer.TYPE;
arrayOfClass[2] = String.class;
Method method = localClass.getMethod("checkOp", arrayOfClass);
if (method == null) {
return false;
}
Object[] arrayOfObject1 = new Object[3];
arrayOfObject1[0] = 24;
arrayOfObject1[1] = Binder.getCallingUid();
arrayOfObject1[2] = context.getPackageName();
int m = ((Integer) method.invoke(object, arrayOfObject1));
return m == AppOpsManager.MODE_ALLOWED;
} catch (Exception ex) {
}
return false;
}
在判断没有需要申请的权限的情况下,需要跳转到对应权限开启页面,由用户手动打开,本文罗列几个常用权限通知栏读取权限、无障碍权限以及悬浮窗权限的开启方法:
通知栏读取权限界面开启方法:
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
无障碍权限界面开启方法:
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
悬浮窗权限界面开启方法:
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + packageName)); // packageName为对应应用包名
startActivity(intent);
首页需要判断是否已具有要申请的权限,这个类别的以通知栏显示权限的判断方法为例:
是否具有通知栏显示权限判断方法:
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static boolean areNotificationsEnabled(Context context) {
boolean flag = true;
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= 24) {
try {
Class cls = Class.forName("android.app.NotificationManager");
Method method = cls.getDeclaredMethod("areNotificationsEnabled");
method.setAccessible(true);
flag = (boolean) method.invoke(manager,new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
} else if (Build.VERSION.SDK_INT >= 19) {
Object appOps = context.getSystemService("appops");
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
try {
Class> appOpsClass = Class.forName("android.app.AppOpsManager");
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE,
Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int) opPostNotificationValue.get(Integer.class);
int v = (int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg);
return v == 0;
} catch (Exception e) {
return true;
}
}
return flag;
}
打开应用对应的详情设置页面:
private void toSettingDetail() {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package", Constants.PACKAGE_NAME, null));
} else if (Build.VERSION.SDK_INT <= 8) {
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails");
intent.putExtra("com.android.settings.ApplicationPkgName", packageName); // packageName为对应应用包名
}
startActivity(intent);
}