manifest

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 静态声明+动态申请存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- andorid 30和30之后 静态声明+动态申请存储权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />
<!-- andorid 30之前 静态声明的蓝牙权限 -->
<uses-permission
    android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission
    android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />
<!-- 使用蓝牙还需要 静态声明+动态申请位置(前台位置)权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 后台位置权限(暂未确定使用的场景) -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- andorid 30之后 静态声明+动态申请的蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

PermissionUtil

package com.yunqi.thamtu.utils

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.os.Environment
import android.provider.Settings
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.lib.common.constant.Constants
import com.lib.common.util.SharedPre


/**
 * @Author:  Jin
 * @Description: 权限管理类
 * @CreateDate: 2023/2/28  14:09
 */
class PermissionUtil {
    companion object {
        /**
         * Android 6(23)及以上系统,需要动态申请 存储权限集合
         */
        private val PERMISSION_STORAGE_ARRAY = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)

        /**
         * Android 11(30)及以上系统,需要动态申请 存储权限
         */
        private val PERMISSION_MANAGE_STORAGE = arrayOf(Manifest.permission.MANAGE_EXTERNAL_STORAGE)

        /**
         * Android 6及以上系统,需要动态申请 位置权限集合
         */
        private val PERMISSION_LOCATION_ARRAY = arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)

        /**
         * Android 12及以上系统,需要动态申请 新增的蓝牙权限集合
         */
        private val PERMISSION_BLUETOOTH_ARRAY = arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_ADVERTISE,
                Manifest.permission.BLUETOOTH_CONNECT)

        /**
         * 全部权限集合
         */
        val PERMISSION_ARRAY = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT)

        const val REQUEST_CODE = 101

        /**
         * 检测存储权限是否授权
         */
        @JvmStatic
        fun checksStoragePermission(context: Context): Boolean {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
                // Android 11(30)及以上系统,无需动态申请该权限
                return true
            }
            return checksPermission(context, PERMISSION_STORAGE_ARRAY)
        }

        /**
         * 检测蓝牙权限是否授权
         */
        @JvmStatic
        fun checksBluetoothPermission(context: Context): Boolean {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S) {
                // Android 12(31)及以下系统,无需动态申请权限
                return true
            }
            return checksPermission(context, PERMISSION_BLUETOOTH_ARRAY)
        }

        /**
         * 检测位置权限是否授权
         */
        @JvmStatic
        fun checksLocationPermission(context: Context): Boolean {
            return checksPermission(context, PERMISSION_LOCATION_ARRAY)
        }

        /**
         * 检测android 11 存储权限是否授权
         */
        @JvmStatic
        fun checksManageStoragePermission(): Boolean {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) {
                // Android 11(30)及以下系统,无需动态申请权限
                return true
            }
            return Environment.isExternalStorageManager()
        }

        private fun checksPermission(context: Context, array: Array<String>): Boolean {
            var granted = true
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
                // Android 6(23)及以下系统,无需动态申请权限
                return granted
            }
            for (permission in array) {
                if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    granted = false
                }
            }
            return granted
        }

        /**
         * 申请存储权限
         */
        @JvmStatic
        fun requestStoragePermission(context: Activity) {
            requestPermissions(context, PERMISSION_STORAGE_ARRAY)
        }

        /**
         * 申请android 11 存储权限
         */
        @JvmStatic
        fun requestManageStoragePermission(context: Activity) {
            val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
            intent?.data = Uri.parse("package:" + context.packageName)
            context.startActivity(intent)
        }

        /**
         * 申请蓝牙权限
         */
        @JvmStatic
        fun requestBluetoothPermission(context: Activity) {
            requestPermissions(context, PERMISSION_BLUETOOTH_ARRAY)
        }

        private fun requestPermissions(context: Activity, array: Array<String>) {
            ActivityCompat.requestPermissions(context, array, REQUEST_CODE)
        }

        @JvmStatic
        fun containsBluetooth(permission: String): Boolean {
            return listOf(*PERMISSION_BLUETOOTH_ARRAY).contains(permission)
        }

        /**
         * 拒绝存储权限
         * @return true已拒绝 false 未拒绝
         */
        @JvmStatic
        fun deniedStoragePermission(): Boolean {
            return SharedPre.getInstance().getBoolean(Constants.PERMISSION_STORAGE_DENIED)
        }

        @JvmStatic
        fun deniedBluetoothPermission(): Boolean {
            return SharedPre.getInstance().getBoolean(Constants.PERMISSION_BLUETOOTH_DENIED)
        }
    }
}

MainActivity

/**
 * 申请系统权限流程:
 * 1.用户点击,
 * 2.检测权限,拒绝
 * 3.弹窗提示用户申请权限
 * 4.用户点击ok,判断 用户拒绝=0,动态申请系统权限
 * 5.用户拒绝权限,申请系统权限失败,记录用户拒绝+1
 * end
 * 6.用户再次点击,
 * 7.检测权限,拒绝
 * 8.弹窗提示用户申请权限
 * 9.用户点击ok,判断 用户拒绝>0,跳转系统设置页,让用户手动开启权限
 * end
 * 后续用户点击 重复6~9
 */
private void initBluetoothPermission(){
    if (!PermissionUtil.checksBluetoothPermission(this)) {
        DialogManager.getInstance().showDialog(getString(R.string.bluetooth_tip1), this, () -> {
            if (PermissionUtil.deniedBluetoothPermission()) {
                // 用户已拒绝权限,跳转系统设置页
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                        .setData(Uri.fromParts("package", getPackageName(), null));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left);
            } else {
                // 用户未拒绝权限,弹框动态申请权限
                PermissionUtil.requestBluetoothPermission(this);
            }
        });
        return;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (PermissionUtil.REQUEST_CODE == requestCode) {
        for (int i = 0; i < permissions.length; i++) {
            String permission = permissions[i];
            int grantResult = grantResults[i];
            LogUtil.i(TAG, "permission: " + permission + ",grantResult:" + grantResult);
            if (PermissionUtil.containsBluetooth(permission)) {
                if (grantResult == PackageManager.PERMISSION_DENIED) {
                    SharedPre.getInstance().putBoolean(Constants.PERMISSION_BLUETOOTH_DENIED, true);
                } else {
                    // 同意权限,扫码蓝牙
                    scanDevice();
                }
            }
        }
    }
}

//*********************************************************************

// 判断是否需要动态申请android 11 以上存储权限
if (!PermissionUtil.checksManageStoragePermission()) {
    DialogManager.getInstance().showDialog(getString(R.string.storage_tip1), this, () ->
            PermissionUtil.requestManageStoragePermission(this));
    return;
}
// 判断是否需要动态申请存储权限
if (!PermissionUtil.checksStoragePermission(this)) {
    DialogManager.getInstance().showDialog(getString(R.string.storage_tip1), this, () -> {
        if (PermissionUtil.deniedStoragePermission()) {
            // 用户已拒绝权限,跳转系统设置页
            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                    .setData(Uri.fromParts("package", getPackageName(), null));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left);
        } else {
            // 用户未拒绝权限,弹框动态申请权限
            PermissionUtil.requestStoragePermission(this);
        }
    });
    return;
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (PermissionUtil.REQUEST_CODE == requestCode) {
        for (int i = 0; i < permissions.length; i++) {
            String permission = permissions[i];
            int grantResult = grantResults[i];
            LogUtil.i(TAG, "permission: " + permission + ",grantResult:" + grantResult);
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                SharedPre.getInstance().putBoolean(com.lib.common.constant.Constants.PERMISSION_STORAGE_DENIED, true);
            }
        }
    }
}