Android合规获取设备权限篇

1.概述

大概是去年收到相关部门的报告,说app频繁弹出权限申请弹框。刚开始不太理解频繁弹出,然后看了一些大厂app,他们对申请设备权限这个增加处理。大概是这样子的,比如设备读存储权限,

第一种未作处理前是这样,当你用到存储权限会打开权限弹框,当你点击拒绝使用时,再次用到存储权限还会弹出打开权限的弹框。第一种做法会已经不符合安全合规了。

第二种做法,当你使用用到存储权限是,会弹出打开权限的弹框,当你点击拒绝,再次用到存储权限是会弹出另一个提示弹框,让你去系统打开app权限,另外当申请权限是还会增加一个权限说明弹框。第二种做法符合安全合规。如下图所示(图片来源网络,侵删)

Android合规获取设备权限篇_第1张图片Android合规获取设备权限篇_第2张图片

2.实现 

1.实现思路

Android合规获取设备权限篇_第3张图片

 2.代码

1.工具类&回调

 import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import androidx.core.content.ContextCompat;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.wangyi.Utils.ChatConstant;
import com.widget.ConfimDialog;
import com.widget.PermissionsDescriptionDialog;
import java.util.ArrayList;
import java.util.List;
 
public class PermissionsUtlis {
    /**
     * 判断是否缺少权限,利用SP存储记录
     */
    public static boolean lacksPermissionNew(Context mContext, String permission) {
        
        if (SpUtil.getBoolean(mContext, ChatConstant.APP_WRITE_EXTERNAL, true)) {
            PermissionsDescriptionDialog.getInstance()
                    .setTitle("存储权限使用说明")
                    .setContent("XX正在申请读取您的存储权限,缓存并同步本地内容,同时保障服务流畅。拒绝或取消不影响您使用XX其他服务。")
                    .show(mContext);
            SpUtil.putBoolean(mContext, ChatConstant.APP_WRITE_EXTERNAL, false);
            return true;
        }
        return !(ContextCompat.checkSelfPermission(mContext, permission) ==
                PackageManager.PERMISSION_DENIED);
    }

    public static void cunChuPermissisons(Context context, String permission, PermissisonsListener permissisonsListener) {
        if (lacksPermissionNew(context, permission)) {
            RxPermissions rxPermissions = new RxPermissions((Activity) context);
            rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .subscribe(aBoolean -> {
                        PermissionsDescriptionDialog.getInstance().hide();
                        if (aBoolean) {
                            permissisonsListener.Permissisons(true);
                        } else {
                            PermissionsUtlis.cunChuPermissisons(context);
                        }

                    });
        } else {
            PermissionsUtlis.cunChuPermissisons(context);
        }


    }

    public static void cunChuPermissisons(Context context) {
        ConfimDialog.getInstance().setTitle("XX需要申请存储权限")
                .setContent("XX正在申请读取您的存储权限同时保障服务流畅。拒绝或取消不影响您使用XX其他服务。")
                .setTvCancel("取消")
                .setTvConfim("去设置")
                .setOperateListener(new ConfimDialog.OnOperateListener() {
                    @Override
                    public void onSure() {
                        //引导用户至设置页手动授权
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", "com.包名", null);
                        intent.setData(uri);
                        context.startActivity(intent);

                    }

                    @Override
                    public void onCancel() {

                    }
                }).show(context);
    }
 
     
}
 
 
public interface PermissisonsListener {
    void Permissisons(boolean flag);
}

2.弹框 &布局

package com.widget;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

 


public class PermissionsDescriptionDialog {
    private static PermissionsDescriptionDialog minstance;
    public static Dialog dialog;
    private static Handler handler = new Handler(Looper.getMainLooper());
    private String content = "";
    private boolean flag;
    private String title;

    private OnOperateListener operateListener;

    public static PermissionsDescriptionDialog getInstance() {
        minstance = new PermissionsDescriptionDialog();
        return minstance;
    }

    public void show(final Context context) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                }
                dialog = new Dialog(context, R.style.MyDialogStyle);

                View view = View.inflate(context, R.layout.alert_permissons_dialog, null);
                dialog.setContentView(view);
                initView(view);
                dialog.setCancelable(false);
                Window window = dialog.getWindow(); //得到对话框
                // window.setWindowAnimations(R.style.dialogWindowAnim); //设置窗口弹出动画
                WindowManager.LayoutParams lp = window.getAttributes();
                if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {//横屏
                    lp.width = getScreenHeight(context) / 20 * 17;
                } else {
                    lp.width = getScreenWidth(context) / 20 * 17;
                }
                window.setGravity(Gravity.TOP);
                window.setAttributes(lp);
                dialog.show();
            }
        });
    }

    private void initView(View view) {
        TextView tvTvitle = view.findViewById(R.id.tv_tvitle);
        if (!TextUtils.isEmpty(title)) {
            tvTvitle.setText(title);
            tvTvitle.setVisibility(View.VISIBLE);
        } else {
            tvTvitle.setVisibility(View.GONE);
        }
        TextView tvContent = (TextView) view.findViewById(R.id.tv_content);
        tvContent.setText(content);
    }

    public void hide() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (null == dialog)
                    return;
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                    dialog = null;
                }
            }
        });
    }

    public void hideDelayed(long delayMillis) {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (null == dialog)
                    return;
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                    dialog = null;
                }
            }
        }, delayMillis);
    }



    public PermissionsDescriptionDialog setContent(String content) {
        this.content = content;
        return minstance;
    }
    public PermissionsDescriptionDialog setTitle(String title) {
        this.title = title;

        return minstance;
    }
    public PermissionsDescriptionDialog setContent(String content, boolean flag) {
        this.content = content;
        this.flag = flag;
        return minstance;
    }





    public PermissionsDescriptionDialog setOperateListener(OnOperateListener operateListener) {
        this.operateListener = operateListener;
        return minstance;
    }

    public interface OnOperateListener {
        void onSure();

        void onCancel();
    }

    /**
     * 获取屏幕分辨率宽
     */
    public static int getScreenWidth(Context context) {
        DisplayMetrics dm = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
        Log.i("widthPixels", dm.widthPixels + "");
        return dm.widthPixels;

    }

    /**
     * 获取屏幕分辨率高
     */
    public static int getScreenHeight(Context context) {
        DisplayMetrics dm = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
        return dm.heightPixels;
    }

}





@android:color/transparent
@null
true
true
true
@null
@android:style/Animation.Dialog
true

3.提示去系统打开app主要思路

 public static void cunChuPermissisons(Context context) {
        ConfimDialog.getInstance().setTitle("XX需要申请存储权限")
                .setContent("XX正在申请读取您的存储权限,,缓存并同步本地内容,同时保障服务流畅。拒绝或取消不影响您使用XX其他服务。")
                .setTvCancel("取消")
                .setTvConfim("去设置")
                .setOperateListener(new ConfimDialog.OnOperateListener() {
                    @Override
                    public void onSure() {
                        //引导用户至设置页手动授权,写好对应的包名
                        Intent intent = new 
                        Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", "com.包名", null);
                        intent.setData(uri);
                        context.startActivity(intent);

                    }

                    @Override
                    public void onCancel() {

                    }
                }).show(context);
    }

4.功能调用

 PermissionsUtlis.cunChuPermissisons(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, new PermissisonsListener() {
                    @Override
                    public void Permissisons(boolean flag) {
                        if (flag) {
                            //TODO 实现你需要的逻辑
                        }

                    }
                });

5.使用到的三方组件

 RxPermissions的库github地址:https://github.com/tbruyelle/RxPermissions

每个做开发都希望可以给用户带来更好的体验,前提是保证用户隐私。后续也会发表一些其他回归安全问题的方案,比如漏洞和信息安全等相关文章。部分弹框没详细写,可以结合自己代码进行完善。

你可能感兴趣的:(Android安全整改,android)