Android APP隐私权限整改

一、背景简介

前段时间,国内对于安卓APP隐私问题做了一波整改,我们的APP也做了一些整改,现在分块抽空整理出来吧

二、整改项目

1、隐私权限整改

主要是针对现在市场上APP各种乱申请权限,获取用户隐私的行为,整理了一个工具类(主要参考云闪付APP的业务逻辑),如下:


public class PermissionUtils {

    
    /

    /**
     * 当前APP需要动态获取的运行时权限
     */
    private static String[] partnerAppPermissions = {
            Manifest.permission.CAMERA,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
//            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.ACCESS_FINE_LOCATION,
//            Manifest.permission.ACCESS_COARSE_LOCATION
    };

    private static Map<String, String> getPermissionNameMap() {
        Map<String, String> permissionNameMap = new HashMap<>();
        permissionNameMap.put(Manifest.permission.CAMERA, "相机");
        permissionNameMap.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, "存储");
//        permissionNameMap.put(Manifest.permission.READ_EXTERNAL_STORAGE, "存储");
        permissionNameMap.put(Manifest.permission.ACCESS_FINE_LOCATION, "定位");
//        permissionNameMap.put(Manifest.permission.ACCESS_COARSE_LOCATION, "定位");
        return permissionNameMap;
    }

    private static Map<String, String> getPermissionDesMap() {
        Map<String, String> getPermissionDesMap = new HashMap<>();
        getPermissionDesMap.put(Manifest.permission.CAMERA, "照相设备将被用于****等需要使用相机的功能");
        getPermissionDesMap.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, "手机存储权限将被用于读写文件系统业务,如图片缓存、主要功能的访问与使用");
//        getPermissionDesMap.put(Manifest.permission.READ_EXTERNAL_STORAGE, "手机存储权限将被用于读写文件系统业务,如图片缓存、主要功能的访问与使用");
        getPermissionDesMap.put(Manifest.permission.ACCESS_FINE_LOCATION, "APP需要获取您的位置信息,用于****等需要位置信息的功能");
//        getPermissionDesMap.put(Manifest.permission.ACCESS_COARSE_LOCATION, "APP需要获取您的位置信息,用于*****等需要位置信息的功能");
        return getPermissionDesMap;
    }


    /**
     * 获取运行时权限列表描述及相关授权情况
     */
    public static List<PermissionBean> getPermissionBeanList(BaseActivity baseActivity) {
        return getPermissionBeanList(baseActivity, partnerAppPermissions);
    }

    private static List<PermissionBean> getPermissionBeanList(BaseActivity baseActivity, String[] permissions) {
        if (permissions == null || permissions.length == 0) {
            return null;
        }
        List<PermissionBean> permissionBeanList = new ArrayList<>();
        PermissionBean permissionBean = null;
        Map<String, String> permissionNameMap = getPermissionNameMap();
        Map<String, String> getPermissionDesMap = getPermissionDesMap();
        for (String permission : permissions) {
            permissionBean = new PermissionBean();
            permissionBean.setPermission(permission);
            permissionBean.setPermissionName(permissionNameMap.get(permission));
            permissionBean.setPermissionDes(getPermissionDesMap.get(permission));
            permissionBean.setGranted(checkPermissionBefore(baseActivity, permission));
            permissionBeanList.add(permissionBean);
            permissionBean = null;
        }
        return permissionBeanList;
    }


    /**
     * 获取相机权限
     * 请勿放在Activity的onResume()中,避免反复弹窗
     *
     * @param baseActivity 上下文环境
     * @param listener     权限获取结果监听
     */
    public static void getCameraPermission(BaseActivity baseActivity, PermissionListener listener) {
        int[] applyMsg = new int[]{R.string.camera_permission, R.string.camera_permission_message, R.string.camera_permission_apply};
        String[] permissions = new String[]{Manifest.permission.CAMERA};
        getNeedPermission(baseActivity, listener, applyMsg, permissions);
    }


    /**
     * 获取外部存储权限
     * 请勿放在Activity的onResume()中,避免反复弹窗
     *
     * @param baseActivity 上下文环境
     * @param listener     权限获取结果监听
     */
    public static void getStoragePermission(BaseActivity baseActivity, PermissionListener listener) {
        int[] applyMsg = new int[]{R.string.external_storage_permission, R.string.external_storage_permission_message, R.string.external_storage_permission_apply};
        String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
        getNeedPermission(baseActivity, listener, applyMsg, permissions);
    }


    /**
     * 获取定位权限
     * 请勿放在Activity的onResume()中,避免反复弹窗
     *
     * @param baseActivity 上下文环境
     * @param listener     权限获取结果监听
     */
    public static void getLocationPermission(BaseActivity baseActivity, PermissionListener listener) {
        int[] applyMsg = new int[]{R.string.location_permission, R.string.location_permission_message, R.string.location_permission_apply};
        String[] permissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
        getNeedPermission(baseActivity, listener, applyMsg, permissions);
    }


    /**
     * 需要动态获取的权限申请
     * 请勿放在Activity的onResume()中,避免反复弹窗
     *
     * @param baseActivity 上下文环境
     * @param listener     权限获取结果监听
     * @param applyMsg     依次为:权限,申请原因说明,申请描述
     * @param permissions  危险权限名称
     */
    private static void getNeedPermission(BaseActivity baseActivity, PermissionListener listener, int[] applyMsg, String... permissions) {
        if (applyMsg == null || applyMsg.length != 3) {
            return;
        }
        if (!checkPermissionBefore(baseActivity, permissions)) {
            requestPermissionDialog(baseActivity, applyMsg[0], applyMsg[1], applyMsg[2], listener, permissions);
        } else {
            listener.allow();
        }
    }


    /**
     * 权限检查
     */
    private static boolean checkPermissionBefore(BaseActivity baseActivity, String... permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(baseActivity, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }


    /**
     * 请求权限前的弹窗
     *
     * @param baseActivity    上下文环境
     * @param title           "APP"想访问您的*****
     * @param message         *****权限权限将被用于………的访问与使用
     * @param unauthorizedMsg 权限获取失败的弹窗提示语
     */
    private static void requestPermissionDialog(BaseActivity baseActivity, @StringRes int title, @StringRes int message, @StringRes int unauthorizedMsg,
                                                PermissionListener listener, String... permissions) {
        CustomDialog.Builder builder = new CustomDialog.Builder(baseActivity);
        builder.setTitle(title)
                .setTitleVisible(true)
                .setMessage(message)
                .setPositiveButton("允许", (dialog, which) -> {
                    dialog.dismiss();
                    //请求权限
                    requestPermission(baseActivity, unauthorizedMsg, listener, permissions);
                })
                .setNegativeButton("不允许", (dialog, which) -> dialog.dismiss())
                .setCancelable(false).create().show();
    }


    /**
     * 权限获取结果监听
     */
    public interface PermissionListener {
        void allow();
    }


    /**
     * 权限获取
     */
    private static void requestPermission(BaseActivity baseActivity, @StringRes int unauthorizedMsg, PermissionListener listener, String... permissions) {
        RxPermissions rxPermissions = new RxPermissions(baseActivity);
        Disposable disposable = rxPermissions.request(permissions)
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        if (aBoolean) {
                            //权限获取成功
                            listener.allow();
                        } else {
                            //权限获取失败,弹出授权提示框
                            unauthorizedPoint(baseActivity, unauthorizedMsg);
                        }
                    }
                });
    }


    /**
     * 未授权时,再次点击弹出的授权提示框
     *
     * @param baseActivity 上下文环境
     * @param message      因您未授予APP****权限,该功能无法使用,可在设置中修改
     */
    private static void unauthorizedPoint(BaseActivity baseActivity, @StringRes int message) {
        CustomDialog.Builder builder = new CustomDialog.Builder(baseActivity);
        builder.setTitle(R.string.permission_apply)
                .setTitleVisible(true)
                .setMessage(message)
                .setPositiveButton("去设置", (dialog, which) -> {
                    dialog.dismiss();
                    //跳转权限设置界面
                    gotoSettings(baseActivity);
                })
                .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
                .setCancelable(false).create().show();
    }


    /**
     * 跳转权限设置界面
     *
     * @param baseActivity 上下文环境
     */
    public static void gotoSettings(BaseActivity baseActivity) {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + baseActivity.getPackageName()));
        baseActivity.startActivity(intent);
    }
}

2、APP隐私政策

  • APP隐私政策内容要注意做好APP权限和隐私的说明
  • 首次使用APP需要弹出隐私政策弹窗
  • 登录APP需要同意隐私政策
  • 隐私政策有更新时需要再次弹出隐私政策弹窗

先简单写这些吧,后续有空再整理补充

 

上一篇:推送代码到远程仓库时无权限,You do not have permission to push to the repository via HTTPS


下一篇:一个符号冲突导致的core分析