Android深入学习之各种隐私权限判断和获取方法总结

Android深入学习之各种隐私权限判断和获取方法总结

  从Android SDK 23 开始, Android就改变了权限的管理模式。对于一些涉及用户隐私的权限则需要用户的授权才可以使用。在此之前,开发者只需要在AndroidManifest.xml中注册,如网络权限、wifi权限等等:权限即可,但是现在除了注册还需要进行手动的授权。
  没有涉及用户隐私的权限申请的方式比较简单,即只需要在AndroidManifest.xml中注册,如网络权限、wifi权限等等:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

  本文主要研究涉及用户隐私权限的判断和申请方法,根据需要用户的操作不同,将这类权限分为三类:

  1. 在应用中直接打开权限申请框,由用户确定打开;
  2. 打开对应权限的设置页面,由用户找到指定应用并勾选;
  3. 打开应用对应的详情设置页面,由用户找到所需权限并打开。

1.在应用中直接打开权限申请框,由用户确定

  以申请手机识别码和申请读写手机存储权限为例,具体代码如下:

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里面声明,否则不生效:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2.打开对应权限的设置页面,由用户找到指定应用再勾选

  首页需要判断是否已具有要申请的权限,不同的权限判断方法不同,需要单独区分权限,本文罗列几个常用权限通知栏读取权限、无障碍权限以及悬浮窗权限的判断方法:
  是否具有通知栏读取权限判断方法:

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<AccessibilityServiceInfo> 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);

3.打开应用对应的详情设置页面,打开需要申请的权限

  首页需要判断是否已具有要申请的权限,这个类别的以通知栏显示权限的判断方法为例:
  是否具有通知栏显示权限判断方法:

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

猜你喜欢

转载自blog.csdn.net/Alexlee1986/article/details/83048627
今日推荐