Permission权限管理——隐私权限具体说明


1、应用场景

   最近在做银行的项目,经过测试中心测试后提出需求,对于隐私权限需要满足以下两点:
       1. 隐私权限的申请应该依托于具体的业务场景,在不需要某个隐私权限的场景内不得申请该权限,非业务需要的隐私权限不得阻碍正常的业务流程。
       2. 需要明确告知用户具体的隐私权限用途,提供用户可以取消权限授权的途径。
针对以上两点我对应用隐私权限的申请进行了整合以及封装,支持在项目中最少量的代码调用,当然这里并不是封装成jar包,用兴趣的同学可以自己封装。

2、实际效果

这里我在权限申请之前告知用户具体的权限用途,文字描述可以根据实际的业务场景来描述,弹窗效果参照了网上的一位兄弟写的,链接如下https://github.com/haiyuKing/PermissionDialog

3、权限申请
1. PermissionDialogUtil类

这个类就是权限申请前的弹窗类,具体说明权限用途,文章最后我会提供源码,这里我们只看一下核心的代码

	/**
    * 初始化权限列表
    * @param filter
    * @param type
    */

   private void initItem(ColorMatrixColorFilter filter,int type){
       //初始化权限列表区域
       View itemView = View.inflate(mContext, R.layout.permission_list_item, null);
       String[] permissionTitles = mContext.getResources().getStringArray(R.array.permission_title);
       String[] permissionInfos = mContext.getResources().getStringArray(R.array.permission_info);
       String title = permissionTitles[type];
       String info = permissionInfos[type];
       ((ImageView) itemView.findViewById(R.id.item_img)).setImageResource(mContext.getResources().obtainTypedArray(R.array.permission_icon).getResourceId(type, 0));
       ((ImageView) itemView.findViewById(R.id.item_img)).setColorFilter(filter);
       ((TextView) itemView.findViewById(R.id.item_title)).setText(title);
       ((TextView) itemView.findViewById(R.id.item_info)).setText(info);

       mListLayout.addView(itemView);
   }
	````
	这里就是权限名称以及相应描述和图片的创建。具体内容我们声明在attrs.xml文件中,内容如下:
	````c
	<!-- 权限图标【可根据实际情况修改】注意位置要和下边title、info相对应 -->
   <integer-array name="permission_icon">
       <item>@drawable/permission_ic_storage</item>
       <item>@drawable/permission_ic_camera</item>
       <item>@drawable/permission_ic_location</item>
       <item>@drawable/permission_ic_phone</item>
       <item>@drawable/permission_ic_phone</item>
       <item>@drawable/permission_ic_micro_phone</item>
       <item>@drawable/permission_ic_calendar</item>
       <item>@drawable/permission_ic_contacts</item>
       <item>@drawable/permission_ic_sms</item>
       <item>@drawable/permission_ic_sensors</item>
   </integer-array>

   <!-- 权限标题【可根据实际情况修改 -->
   <string-array name="permission_title">
       <item>文件存储</item>
       <item>照相机</item>
       <item>位置信息</item>
       <item>本机识别码</item>
       <item>拨打电话</item>
       <item>录制声音</item>
       <item>日程信息</item>
       <item>通讯录</item>
       <item>短信信息</item>
       <item>传感器</item>
   </string-array>

   <!-- 权限说明(这里应该是根据项目的实际情况,简要说明下该权限主要用于什么场景或者功能)【可根据实际情况修改】 -->
   <string-array name="permission_info">
       <item>允许应用读取、写入外部存储,应用于版本更新、相册功能</item>
       <item>允许应用访问摄像头进行拍照,应用于个人头像功能</item>
       <item>允许应用获取定位信息</item>
       <item>允许应用获取本机识别码,应用于账号和手机的绑定关系,该权限为必须权限</item>
       <item>允许应用内拨打电话,应用于联系客服</item>
       <item>允许应用通过手机或耳机的麦克风录制声音</item>
       <item>允许应用读取用户的日程信息</item>
       <item>允许应用访问联系人通讯录信息</item>
       <item>允许应用获取短信信息</item>
       <item>允许应用获取传感器信息</item>
   </string-array>

注意标题、图像、描述位置是一一对应的,这里权限的位置在后边我们会用到。

2. PermissionUtils类
 	 public static void checkPermission(final Activity activity, final OnPermissionListener grantedListener, final String... permissions) {
        boolean havePermission = true;
        final ArrayList<Integer> types = new ArrayList<Integer>();
        for (String permission : permissions) {
            switch (permission) {
                case Manifest.permission.WRITE_EXTERNAL_STORAGE:
                case Manifest.permission.READ_EXTERNAL_STORAGE:
                    if (!types.contains(PermissionType.STORAGE)) {
                        types.add(PermissionType.STORAGE);
                    }
                    break;
                case Manifest.permission.CAMERA:
                    if (!types.contains(PermissionType.CAMERA)) {
                        types.add(PermissionType.CAMERA);
                    }
                    break;
                case Manifest.permission.READ_PHONE_STATE:
                    if (!types.contains(PermissionType.PHONE)) {
                        types.add(PermissionType.PHONE);
                    }
                    break;
                case Manifest.permission.CALL_PHONE:
                    if (!types.contains(PermissionType.CALLPHONE)) {
                        types.add(PermissionType.CALLPHONE);
                    }
                    break;
                case Manifest.permission.ACCESS_COARSE_LOCATION:
                    if (!types.contains(PermissionType.LOCATION)) {
                        types.add(PermissionType.LOCATION);
                    }
                    break;
                case Manifest.permission.READ_SMS:
                case Manifest.permission.SEND_SMS:
                    if (!types.contains(PermissionType.SMS)) {
                        types.add(PermissionType.SMS);
                    }
                    break;
                default:
                    break;
            }
            if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                havePermission = false;
            }
        }
        if (!havePermission) {
            PermissionDialogUtil permissionDialog = new PermissionDialogUtil(activity, types);
            permissionDialog.setOnSureClickListener(new PermissionDialogUtil.OnSureClickListener() {
                @Override
                public void onSureClick() {
                    types.clear();
                    RxPermissions rxPermission = new RxPermissions((FragmentActivity) activity);
                    rxPermission
                            .requestEachCombined(permissions)
                            .subscribe(new Consumer<Permission>() {
                                @Override
                                public void accept(Permission permission) throws Exception {
                                    if (permission.granted) {
                                        grantedListener.onGranted();
                                    } else if (permission.shouldShowRequestPermissionRationale) {
                                        grantedListener.onRefused();
                                    } else {
                                        DialogUtils.showDotAskDialog(activity, new DialogUtils.OnDialogClickListener() {
                                            @Override
                                            public void confirm() {
                                                goSystemStetting(activity);
                                            }

                                            @Override
                                            public void cancel() {
                                            }
                                        });
                                    }
                                }
                            });
                }
            });
            permissionDialog.show();

        } else {
            grantedListener.onGranted();
        }
    }
	````
	在checkPermission方法中,我们比对传递过来的权限,只有需要申请的权限才会显示在弹窗中,PermissionType是一个枚举类如下:
	````
	public interface PermissionType {
    @IntDef({STORAGE, CAMERA, LOCATION, PHONE, CALLPHONE, SMS})
    @Retention(RetentionPolicy.SOURCE)
    @interface Type {
    }

    int STORAGE = 0;
    int CAMERA = 1;
    int LOCATION = 2;
    int PHONE = 3;
    int CALLPHONE = 4;
    int SMS = 8;
	}

注意声明的枚举后边的数字要等同于上边attrs中权限的实际位置,例如sms在attrs中就处于第八个,当然这个顺序可以自己更改,只要能对应上即可。
    权限的申请流程我使用了RxPermissions的requestEachCombined方法,这个方法会将申请的权限当成一组权限,只有全部允许permission.granted才会为true,permission.shouldShowRequestPermissionRationale方法可以用来判断用户是否设置了不在提醒按钮,对于这种权限我的处理方法是下次申请直接跳转到设置页面中,让用户自行开启。DialogUtils.showDotAskDialog弹窗就是用来实现这个功能,内容简单不做介绍,源码中有。

3. 具体调用

我们只需要在需要权限申请的位置调用

 	PermissionUtils.checkPermission(this, new OnPermissionListener() {
                    @Override
                    public void onGranted() {

                    }

                    @Override
                    public void onRefused() {
                        Toast.makeText(getActivity(), "用户拒绝", Toast.LENGTH_SHORT).show();
                    }
                }, Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.READ_SMS);

    在onGranted()方法中处理允许流程,onRefused()处理拒绝流程,如果是在Fragment中调用我们只需要将传入的this换成getActivity()。具体需要申请的权限个数不固定,也可以传入一个。

4、权限管理

对于应用中需要的隐私权限我们需要对他们进行管理,效果参照支付宝。

实现也很简单,我们只需要在PermissionUtils类中增加一个checkPermission()方法,这个方法是用来判断权限是否申请。

 public static boolean checkPermission(final Activity activity, String... permissions) {
        for (String permission : permissions) {
            if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

然后我们在权限管理页面的onResume()方法中进行状态的判断即可。

 @Override
    public void onResume() {
        super.onResume();
        if (PermissionUtils.checkPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            ps_storage.setTvRightStyle(getResources().getString(R.string.tv_right), getResources().getColor(R.color.title_text2));
        } else {
            ps_storage.setTvRightStyle(getResources().getString(R.string.tv_right_no), getResources().getColor(R.color.link_color));
        }
        if (PermissionUtils.checkPermission(getActivity(), Manifest.permission.CAMERA)) {
            ps_camera.setTvRightStyle(getResources().getString(R.string.tv_right), getResources().getColor(R.color.title_text2));
        } else {
            ps_camera.setTvRightStyle(getResources().getString(R.string.tv_right_no), getResources().getColor(R.color.link_color));
        }
        if (PermissionUtils.checkPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION)) {
            ps_location.setTvRightStyle(getResources().getString(R.string.tv_right), getResources().getColor(R.color.title_text2));
        } else {
            ps_location.setTvRightStyle(getResources().getString(R.string.tv_right_no), getResources().getColor(R.color.link_color));
        }
        if (PermissionUtils.checkPermission(getActivity(), Manifest.permission.CALL_PHONE)) {
            ps_phone.setTvRightStyle(getResources().getString(R.string.tv_right), getResources().getColor(R.color.title_text2));
        } else {
            ps_phone.setTvRightStyle(getResources().getString(R.string.tv_right_no), getResources().getColor(R.color.link_color));
        }
        if (PermissionUtils.checkPermission(getActivity(), Manifest.permission.READ_SMS)) {
            ps_sms.setTvRightStyle(getResources().getString(R.string.tv_right), getResources().getColor(R.color.title_text2));
        } else {
            ps_sms.setTvRightStyle(getResources().getString(R.string.tv_right_no), getResources().getColor(R.color.link_color));
        }
    }

跳转手机设置中心的方法如下:

 	public void goSystemStetting() {
        Intent intent = getAppDetailSettingIntent();
        startActivity(intent);
    }
    private Intent getAppDetailSettingIntent() {
        Intent localIntent = new Intent();
        localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
            localIntent.setData(Uri.fromParts("package", getActivity().getPackageName(), null));
        } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.FROYO) {
            localIntent.setAction(Intent.ACTION_VIEW);
            localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
            localIntent.putExtra("com.android.settings.ApplicationPkgName", getActivity().getPackageName());
        }
        return localIntent;
    }
5、项目地址

https://github.com/li-21/PermissionU

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值