Android用户权限之记录是否调起弹窗

记录一个最近在开发中遇到的奇葩需求,需求内容主要是:

记录在申请权限的时候客户端的行为,包括是否弹出了系统权限,以及弹出情况下用户是否同意授权

对于是否弹窗,我们都知道Android权限的几种情况:

  1. 设置中处于询问的状态(初次请求权限或者已拒绝权限但没有点击不再提示(也有部分手机拒绝了就不再提示)或者手动到设置中设置),这种会弹出权限申请框
  2. 设置中处于同意的状态,不弹起申请框直接通过申请
  3. 设置中处于拒绝的状态,不弹起申请框直接拒绝申请

我们回过头看一下需求,弹出的情况下用户是否同意授权这个其实很好判断,主要是是否弹出了授权框,我们通过系统的api并不能直接拿到这个结果。因为弹出申请框是一个系统行为,并不是我们自己定义的一个弹框。

我们可以先回顾一下申请权限的原始步骤:

requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100);

然后在回调中处理申请结果:

@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {  }

客户端做的事情起其实只有

  1. 发起申请权限的动作
  2. 处理申请权限的结果
    对于整个申请过程都是透明的,客户端并不知道是否有弹窗

所以我们这里只有曲线救国了,我们分析一下现在有的几种可能性:

  1. 第一次申请权限(包括手动修改设置为询问状态)
  2. 已拒绝但是没有点击不再显示
  3. 已拒绝也已点击不再显示
  4. 已通过授权

这里第1,2两种情况是会发起授权的,34两种情况不会再发起授权,所以我们的重点在于如何区分这四种状态。

第四种很好区分,通过ContextCompat.checkSelfPermission()方法就可以检测出
这里介绍一下另一个方法:

ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)

这个方法返回一个boolean值,表示之前是否已经被拒绝过但是还没有点击不再显示。所以通过这个方法可以区分出来第二种情况。

那我们就剩下第一种和第三种如何区分了,第一种的话其实我们可以发现有一个规律,第一次去请求权限是肯定不会有不再显示按钮的,包括重新将状态设置为询问,这个大家可以测试一下。

所以我们可以根据请求结果做一下文章

请求结果是成功的

这种肯定就是第一种情况,因为如果是第三种情况是肯定不会请求成功的(第二种和第四种在请求结果之前就已经筛选出去了,所以不用考虑)

请求结果失败

这里就可以再次用上这个方法了

ActivityCompat.shouldShowRequestPermissionRationale

如果是第一种情况,那么这个方法应该是返回true,因为第一次是没有不再显示这个按钮的,如果返回false那么就是第三种情况,这样就区分出来了

经过这个需求只能说对于权限系统理解更深了吧。
/(ㄒoㄒ)/~~

附上代码:
定义一个enum表示上面的四种状态

public enum PermissionState{    /**     * 询问状态,展示弹窗     */    ASKING(true),    /**     * 点击拒绝,未点击不再显示,展示弹窗     */    MORE_REQUEST(true),    /**     * 已同意,不展示弹窗     */    HAS_GRANTED(false),    /**     * 点击拒绝,已点击不再显示,不展示弹窗     */    DEFINED(false);    private boolean mShowDialog;    PermissionState(boolean showDialog){        mShowDialog = showDialog;    }    public boolean isShowDialog(){        return mShowDialog;    }}

定义一个结果接收权限申请结果:

public interface PermissionListener {    void onSucceed(int var1, @NonNull List var2);    void onFailed(int var1, @NonNull List var2);}

新增一个装饰类处理结果进行区分:

public class PermissionTrackerListener implements PermissionListener {    private Activity mActivity;    private PermissionListener mListener;    /**     * 权限列表     */    private List mNeedTrackPermissionList;    /**     * 当前几个权限的状态     */    private List mNeedTrackPermissionStateList;    public PermissionTrackerListener(Activity activity, PermissionListener listener){        mActivity = activity;        mListener = listener;        mNeedTrackPermissionList = new ArrayList<>();        mNeedTrackPermissionStateList = new ArrayList<>();    }    @Override    public void onSucceed(int i, @NonNull List list) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            for (int j = 0; j < mNeedTrackPermissionList.size(); j++) {                PermissionState state = mNeedTrackPermissionStateList.get(j);                String permission = mNeedTrackPermissionList.get(j);                if (state == null){                    state = PermissionState.ASKING;                }                if (state == PermissionState.ASKING || state == PermissionState.MORE_REQUEST) {                    Toast.makeText(mActivity, "已弹窗埋点: " + permission, Toast.LENGTH_SHORT).show();                    Toast.makeText(mActivity, "点击同意埋点: " + permission, Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(mActivity, "未弹窗埋点: " + permission, Toast.LENGTH_SHORT).show();                }            }        }        mListener.onSucceed(i, list);    }    @Override    public void onFailed(int i, @NonNull List list) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            for (int j = 0; j < mNeedTrackPermissionList.size(); j++) {                String permission = mNeedTrackPermissionList.get(j);                boolean denied = list.contains(permission);                PermissionState state = mNeedTrackPermissionStateList.get(j);                if (state == null) {                    if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permission)) {                        state = PermissionState.ASKING;                    } else {                        state = PermissionState.DEFINED;                    }                }                if (state == PermissionState.ASKING || state == PermissionState.MORE_REQUEST) {                    Toast.makeText(mActivity, "已弹窗埋点: " + permission, Toast.LENGTH_SHORT).show();                    if (denied) {                        Toast.makeText(mActivity, "点击拒绝埋点: " + permission, Toast.LENGTH_SHORT).show();                    } else {                        Toast.makeText(mActivity, "点击同意埋点: " + permission, Toast.LENGTH_SHORT).show();                    }                } else {                    Toast.makeText(mActivity, "未弹窗埋点: " + permission, Toast.LENGTH_SHORT).show();                }            }        }        mListener.onFailed(i, list);    }    /**     * 申请权限之前调用     * @param list  需要申请的权限     */    public void preRequestPermission(@NonNull List list){        mNeedTrackPermissionList = list;        if (mNeedTrackPermissionList.isEmpty()){            return;        }        for (String permission : mNeedTrackPermissionList) {            PermissionState state = null;            if (ActivityCompat.checkSelfPermission(mActivity, permission) == PackageManager.PERMISSION_GRANTED){                state = PermissionState.HAS_GRANTED;            } else if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permission)){                state = PermissionState.MORE_REQUEST;            }            mNeedTrackPermissionStateList.add(state);        }}}

更多相关文章

  1. Ubuntu上开发Android,手机调试配置
  2. Android中使用Notification并通过点击Notification启动启动通知
  3. Android实现通过浏览器点击链接打开本地应用(APP)并拿到浏览器传递
  4. Android(安卓)录制手机屏幕视频生成GIF图片实例详解
  5. 用android做的一个简单的电话拨号器的错误的解决(用android模拟两
  6. 安卓TV开发(九) Android模拟事件 遥控器变身成鼠标来操作TV
  7. android 验证码按钮点击,判断网络和匹配手机号并自动更新时间;
  8. android 事件分发,解决由于listview中实时刷新,导致子view点击事件
  9. Android跨程序共享数据

随机推荐

  1. Android(安卓)Studio 关联项目软链接
  2. Android(安卓)Studio 获取数字签名信息(SH
  3. android flutter打包 apk 及接facebook
  4. Android(安卓)studio 编译失败Error:Coul
  5. Hierarchy Viewer 帮你分析应用程序UI布
  6. Android(安卓)批量读取APK 包名,版本信息
  7. [Android(安卓)Studio导入第三方类库方法
  8. Android(安卓)Studio 引入 Fresco
  9. Android(安卓)读SIM卡信息
  10. Android(安卓)静默更新apk