Android 6.0动态权限大总结(续集)

前段时间写过一篇文章Android 6.0动态权限大总结,参考了网上众多博客文章的结晶。

在公司项目中应用时,还是被同事质疑了。当然了,是好事。后来在同事修改的基础上,我又进行了优化处理,现在看了是好用多了。下面分享下最后的工具类,和用法。

我就不上传代码了,直接贴给你看。


一、工具类:

DynamicPermissionCompat.java

/**
 * @Author: duke
 * @DateTime: 2017-06-08 10:51
 * @Description: android 6.0 权限适配
 */
public class DynamicPermissionCompat {
    private Activity activity;
    private String[] permissions;
    private int requestCode;
    private OnPermissionListener listener;

    private DynamicPermissionCompat(Activity activity) {
        this.activity = activity;
    }

    /**
     * 获取实例对象(非单例)
     *
     * @param activity
     * @param permissionManager 此参数的目的是为了避免重复创建
     * @return
     */
    public static DynamicPermissionCompat getInstance(Activity activity, DynamicPermissionCompat permissionManager) {
        if (permissionManager != null) {
            return permissionManager;
        }
        if (activity == null) {
            throw new IllegalArgumentException("activity not allow null");
        }
        return new DynamicPermissionCompat(activity);
    }

    public DynamicPermissionCompat setRequestCode(int requestCode) {
        this.requestCode = requestCode;
        return this;
    }

    public DynamicPermissionCompat setPermissions(String... permissions) {
        this.permissions = permissions;
        return this;
    }

    public DynamicPermissionCompat setOnPermissionListener(OnPermissionListener listener) {
        this.listener = listener;
        return this;
    }

    /**
     * 开始申请权限
     */
    public void start() {
        if (!isNeedAdapt()) {
            succeed();
        } else if (permissions == null) {
            failed(new ArrayList<String>());
        } else {
            String[] deniedPermissions = getDeniedPermissions(activity, permissions);
            if (deniedPermissions.length > 0) {
                //has没有授权
                ActivityCompat.requestPermissions(activity, deniedPermissions, requestCode);
            } else {
                //全部都已经授权过了
                succeed();
            }
        }
    }

    /**
     * 是否需要6.0权限适配
     *
     * @return
     */
    private static boolean isNeedAdapt() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    /**
     * 向用户申请打开系统设置和浮框权限
     *
     * @param activity
     * @return true:申请成功,false:申请失败
     */
    @TargetApi(Build.VERSION_CODES.M)
    public static boolean adaptWriteSettingAndOverlay(Activity activity) {
        if (activity == null) {
            //失败,不可以继续执行
            return false;
        }
        if (!isNeedAdapt()) {
            //不需要适配,确定可以继续走
            return true;
        }
        if (Settings.canDrawOverlays(activity) && Settings.System.canWrite(activity)) {
            //已经适配,确定可以继续走
            return true;
        }
        if (!Settings.canDrawOverlays(activity)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + activity.getPackageName()));
            activity.startActivity(intent);
        }
        if (!Settings.System.canWrite(activity)) {
            Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
                    Uri.parse("package:" + activity.getPackageName()));
            activity.startActivity(intentWrite);
        }
        activity.finish();
        android.os.Process.killProcess(android.os.Process.myPid());
        //不确定是否打开权限,不可以继续执行,等待下次启动app来判断
        return false;
    }

    /**
     * 是否需要申请权限
     *
     * @param context
     * @param permissions
     * @return
     */
    public static boolean isNeedRequestGrantPermission(Context context, String... permissions) {
        if (context == null
                || permissions == null
                || permissions.length == 0
                || !isNeedAdapt()
                || getDeniedPermissions(context, permissions).length == 0) {
            return false;
        }
        return true;
    }

    /**
     * 获取未授权列表
     *
     * @return
     */
    private static String[] getDeniedPermissions(Context context, String... permissions) {
        if (context == null || permissions == null || permissions.length == 0) {
            return new String[]{};
        }
        ArrayList<String> deniedList = new ArrayList<>(1);
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                deniedList.add(permission);
            }
        }
        return deniedList.toArray(new String[deniedList.size()]);
    }

    /**
     * 向用户弹出解释对话框 <br/>
     * *******************************************************************************
     * ** 应用安装后第一次访问,直接返回false;                                     **
     * ** 第一次请求权限时用户拒绝了,下一次返回 true,                             **
     * ** 这时候可以显示一些为什么需要这个权限的说明;                              **
     * ** 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时,返回 false;  **
     * ** 设备的系统设置中禁止当前应用获取这个权限的授权,返回false;             **
     * ** 注意:第二次请求权限时,才会有“不再提醒”的选项,                        **
     * ** 如果用户一直拒绝,并没有选择“不再提醒”的选项,                          **
     * ** 下次请求权限时,会继续有“不再提醒”的选项,并且会一直返回true            **
     * *******************************************************************************
     *
     * @param permissions 需要提示解释的权限申请
     * @return 需要提示:true,不需要:false
     */
    private static boolean shouldShowRationalePermissions(Activity activity, ArrayList<String> permissions) {
        if (!isNeedAdapt()) {
            return false;
        }
        if (activity == null || permissions == null || permissions.size() == 0) {
            return false;
        }
        for (String permission : permissions) {
            boolean rationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
            if (rationale) {
                return true;
            }
        }
        return false;
    }

    /**
     * 权限授权成功
     */
    private void succeed() {
        if (listener != null) {
            listener.success(requestCode);
        }
    }

    /**
     * 用户拒绝了某个或者某些权限
     *
     * @param deniedList
     */
    private void failed(ArrayList<String> deniedList) {
        /**
         * shouldShowRationalePermissions(activity, deniedList):
         * true:是第一次拒绝 ~ 勾选不再提醒之前。
         * false:为拒绝 ~ 勾选不再提醒之后。
         */
        if (!shouldShowRationalePermissions(activity, deniedList)) {
            if (listener != null) {
                listener.alwaysDenied(requestCode, deniedList);
            }
        } else {
            if (listener != null) {
                listener.failAndTipUser(requestCode, deniedList);
            }
        }
    }

    /**
     * 授权的回调
     *
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionsResult(String[] permissions, int[] grantResults) {
        if (permissions.length != grantResults.length) {
            failed(null);
            return;
        }
        ArrayList<String> deniedList = new ArrayList<>(permissions.length);
        for (int i = 0; i < grantResults.length; i++) {
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                deniedList.add(permissions[i]);
            }
        }
        deniedList.trimToSize();
        if (deniedList.isEmpty()) {
            succeed();
        } else {
            failed(deniedList);
        }
    }

    /**
     * 如果用户勾选了不再提示,打开APP设置,引导用户手动开启
     */
    public static void showSetting(Activity activity, int requestCode) {
        if (activity == null) {
            return;
        }
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
        intent.setData(uri);
        activity.startActivityForResult(intent, requestCode);
    }

    public static abstract class OnPermissionListener {
        /**
         * 全部授权成功
         *
         * @param requestCode
         */
        public abstract void success(int requestCode);

        /**
         * 有权限被拒绝,提示用户
         *
         * @param requestCode
         * @param deniedPermissions 被拒绝的权限集合
         */
        public void failAndTipUser(int requestCode, List<String> deniedPermissions) {
        }

        /**
         * 用户拒绝权限,并勾选了下次不再提醒
         *
         * @param requestCode
         * @param deniedPermissions 被拒绝的权限集合
         */
        public void alwaysDenied(int requestCode, List<String> deniedPermissions) {
        }
    }
}


核心东西没变,只是封装优化了。看下面的用法吧:


permissionManager = DynamicPermissionCompat.getInstance(this, permissionManager);
创建工具类对象,建议有一个Activity中创建一个对象就好。

permissionManager.setOnPermissionListener(permissionListener)
        .setPermissions(permissions)
        .setRequestCode(requestCode)
        .start();
以构建者模式,调起授权的代码。你不用判断是否是6.0以上版本,里面的逻辑中判断了,向下兼容的。


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (permissionManager != null) {
        permissionManager.onRequestPermissionsResult(permissions, grantResults);
    }
}
在当前Activity或者bese Activity中重写这个函数,接受回调处理。


授权回调:
private DynamicPermissionCompat.OnPermissionListener permissionListener = new DynamicPermissionCompat.OnPermissionListener() {
    @Override
    public void success(int requestCode) {
        boolean is1 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "other.apk", new File(otherFilePath));
        boolean is2 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "me.apk", new File(myFilePath));
//            ApkManager.installApk(ApkInstallActivity.this, new File(otherFilePath));


    }

    @Override
    public void failAndTipUser(int requestCode, List<String> deniedPermissions) {
        Toast.makeText(ApkInstallActivity.this, "用户拒绝了", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void alwaysDenied(int requestCode, List<String> deniedPermissions) {
        Toast.makeText(ApkInstallActivity.this, "用户勾选了\"不再提醒\"", Toast.LENGTH_SHORT).show();
    }
};

瞧瞧回调函数,里面有三个方法:授权成功、用户拒绝、用户勾选了不再提醒。



+++++++++完了,就这些。与上一版本相比,是不是简单多了?+++++++++++++++++++++++++++++++++++++++++++++++++++++


下面再说说6.0权限的另一部分吧。

其实android6.0动态权限不止普通权限、动态权限两类,还有一个系统等级的权限,这个是没办法动态申请的,但是用户不打开,程序的系统设置、系统浮窗功能就是用不了,比喻修改屏幕亮度、音量等等。如果没有权限的话,你会在控制台上看到TYPE_SYSTEM_ALERT 或 WRITE_SETTINGS等权限提示。

下面提供一个封装的工具类:

SystemSettings.java

/**
 * @Author: duke
 * @DateTime: 2017-06-16 14:25
 * @Description: 系统设置工具类 <br/>
 * 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/>
 * Settings.System -- WRITE_SETTINGS <br/>
 * Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
 * Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
 */
public class SystemSettings {
    public static final String TAG = SystemSettings.class.getSimpleName();

    public static boolean isNeedCompatPermission() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    /**
     * 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限
     *
     * @param context
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    public static boolean hasWritePermission(Context context) {
        if (context == null) {
            return false;
        }
        if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否有 设置系统浮窗 权限
     *
     * @param context
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    public static boolean hasOverlaysPermission(Context context) {
        if (context == null) {
            return false;
        }
        if (!isNeedCompatPermission() || Settings.canDrawOverlays(context)) {
            return true;
        }
        return false;
    }

    @TargetApi(Build.VERSION_CODES.M)
    public static void guideUserWritePermission(Context context) {
        if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {
            return;
        }
        Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
                Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intentWrite);
    }

    @TargetApi(Build.VERSION_CODES.M)
    public static void guideUserOverlaysPermission(Context context) {
        if (context == null || !isNeedCompatPermission() || hasOverlaysPermission(context)) {
            return;
        }
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intent);
    }

    public static String getAndroidId(Context context) {
        if (context == null) {
            return "";
        }
        try {
            return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.getAndroidId() is exception of" + e.getMessage());
            e.printStackTrace();
        }
        return "";
    }

    public static int getAccelerometerRotation(Context context) {
        if (context == null) {
            return 0;
        }
        try {
            return Settings.System.getInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.getAccelerometerRotation() is exception of" + e.getMessage());
            e.printStackTrace();
        }
        return 0;
    }

    public static void putAccelerometerRotation(Context context, int val) {
        if (context == null) {
            return;
        }
        if (!hasWritePermission(context)) {
            // TODO: 2017/6/16 需确定提示文本
            Toast.makeText(context, "暂无屏幕自动切换的权限", Toast.LENGTH_LONG).show();
            guideUserWritePermission(context);
            return;
        }
        try {
            Settings.System.putInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, val);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.putAccelerometerRotation() is exception of" + e.getMessage());
            e.printStackTrace();
        }
    }

    //使用的地方已被注释
    public static int getScreenBrightness(Context context) {
        if (context == null) {
            return 0;
        }
        try {
            return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.getScreenBrightness() is exception of" + e.getMessage());
            e.printStackTrace();
        }
        return 0;
    }

    //使用的地方已被注释
    public static void putScreenBrightness(Context context, int val) {
        if (context == null) {
            return;
        }
        if (!hasWritePermission(context)) {
            // TODO: 2017/6/16 需确定提示文本
            Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();
            guideUserWritePermission(context);
            return;
        }
        try {
            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());
            e.printStackTrace();
        }
    }

    //使用的地方已被注释
    public static int getScreenBrightnessMode(Context context) {
        if (context == null) {
            return 0;
        }
        try {
            return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.getScreenBrightnessMode() is exception of" + e.getMessage());
            e.printStackTrace();
        }
        return 0;
    }

    //使用的地方已被注释
    public static void putScreenBrightnessMode(Context context, int val) {
        if (context == null) {
            return;
        }
        if (!hasWritePermission(context)) {
            // TODO: 2017/6/16 需确定提示文本
            Toast.makeText(context, "暂无设置系统亮度模式的权限", Toast.LENGTH_LONG).show();
            guideUserWritePermission(context);
            return;
        }
        try {
            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, val);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.putScreenBrightnessMode() is exception of" + e.getMessage());
            e.printStackTrace();
        }
    }
}

拿WRITE_SETTINGS权限来,下面大概讲解下核心部分。

public static boolean isNeedCompatPermission() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }
这是6.0的权限,也需要版本判断。


/**
     * 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限
     *
     * @param context
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    public static boolean hasWritePermission(Context context) {
        if (context == null) {
            return false;
        }
        if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {
            return true;
        }
        return false;
    
判断有没有修改系统设置权限的核心方法:Settings.System.canWrite(context)


如果没有呢?

@TargetApi(Build.VERSION_CODES.M)
    public static void guideUserWritePermission(Context context) {
        if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {
            return;
        }
        Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
                Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intentWrite);
    }
通过intent,引导用户去系统设置界面了。

/**
 * 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/>
 * Settings.System -- WRITE_SETTINGS <br/>
 * Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
 * Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
 */
WRITE_SETTINGS权限,其实就是对Settings下的System,Global,Secure的三张表修改的权限。读取不需要权限。

故,像所有Settings.xxx.putxxx()这类方法,都需要权限。建议把所有对Settings下三张表进行读写的操作都集中到这个类里面,统一进行判断处理。例如:

public static void putScreenBrightness(Context context, int val) {
        if (context == null) {
            return;
        }
        if (!hasWritePermission(context)) {
            // TODO: 2017/6/16 需确定提示文本
            Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();
            guideUserWritePermission(context);
            return;
        }
        try {
            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);
        } catch (Exception e) {
            Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());
            e.printStackTrace();
        }
    }


还有一个系统浮窗的权限,正在整理中,后续再发布。







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值