前言
在使用Jetpack Compose开发UI的时候,已经不能用以前在activity的方式申请动态权限了。 甚至连申请权限的思维也不太一样。虽然现在也可以直接申请动态权限。但是,现在的申请动态权限的规范是先要弹出一个弹窗告知用户权限的使用目的,然后再去申请这个权限。所以在Jetpack Compose申请权限最好是跟申请目的对话框捆绑在一起。 这个博客提供申请蓝牙Ble相关权限与相机权限作为参考。
申请蓝牙权限
- import android.Manifest
- import android.app.Activity
- import android.content.Context
- import android.content.pm.PackageManager
- import android.os.Build
- import androidx.activity.compose.rememberLauncherForActivityResult
- import androidx.activity.result.contract.ActivityResultContracts
- import androidx.compose.foundation.layout.Arrangement
- import androidx.compose.foundation.layout.Column
- import androidx.compose.foundation.layout.Row
- import androidx.compose.foundation.layout.Spacer
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.height
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.width
- import androidx.compose.material3.Button
- import androidx.compose.material3.ButtonDefaults
- import androidx.compose.material3.Card
- import androidx.compose.material3.CardDefaults
- import androidx.compose.material3.MaterialTheme
- import androidx.compose.material3.Text
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.mutableStateListOf
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.unit.dp
- import androidx.compose.ui.unit.sp
- import androidx.compose.ui.window.Dialog
- import androidx.core.app.ActivityCompat
- import androidx.core.content.ContextCompat
- import com.yak.bleSdk.log.logE
- private val bluetoothPermissions by lazy {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- arrayOf(
- Manifest.permission.BLUETOOTH_SCAN,
- Manifest.permission.BLUETOOTH_CONNECT,
- Manifest.permission.ACCESS_FINE_LOCATION // 某些情况仍需要
- )
- } else {
- arrayOf(
- Manifest.permission.BLUETOOTH,
- Manifest.permission.BLUETOOTH_ADMIN,
- Manifest.permission.ACCESS_FINE_LOCATION
- )
- }
- }
- /**
- * 检查蓝牙权限
- */
- fun checkBluetoothPermissions(context: Context): Boolean {
- // 检查是否已拥有所有权限
- val hasAllPermissions = bluetoothPermissions.all { permission ->
- ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
- }
- return hasAllPermissions
- }
- /**
- * 请求蓝牙权限
- * @param isRequest 是否请求权限, 这个布尔值可以用来控制申请权限的时机比如按下某个按钮后申请权限,如果不需要选择时机,可以默认为ture
- * 如果需要控制申请时机,请参考:
- * ```kotlin
- * val isRequestBluetoothPermissions = remember { mutableStateOf(false) }
- * RequestBluetoothPermissions(
- * isRequest = isRequestBluetoothPermissions.value,
- * agree = {
- * // 用户同意权限后的操作
- * },
- * refuse = {
- * // 用户拒绝权限后的操作
- * }
- * )
- * Button(
- * onClick = {
- * if(!checkBluetoothPermissions(context)){
- * isRequestBluetoothPermissions.value = true
- * }
- * },
- * ) {
- * Text("点击申请权限", modifier = Modifier.padding(8.dp))
- * }
- * ```
- * @param agree 同意
- * @param refuse 拒绝
- */
- @Composable
- fun RequestBluetoothPermissions(isRequest: Boolean = true, agree: () -> Unit, refuse: () -> Unit) {
- if (!isRequest) {
- return
- }
- val context = LocalContext.current
- val activity = LocalContext.current as Activity
- val allPermissionsGranted = remember { mutableStateOf(false) }
- // 记录被永久拒绝的权限列表
- val permanentlyDeniedPermissions = remember { mutableStateListOf<String>() }
- val permissionLauncher = rememberLauncherForActivityResult(
- ActivityResultContracts.RequestMultiplePermissions()
- ) { permissionsMap ->
- allPermissionsGranted.value = permissionsMap.values.all { it }
- if (allPermissionsGranted.value) {
- // 所有权限都已授予
- "所有权限都已授予".logE()
- agree.invoke()
- } else {
- // 处理权限被拒绝的情况
- "处理权限被拒绝的情况".logE()
- refuse.invoke()
- // 遍历结果,找出被拒绝的权限,并判断是否为永久拒绝
- permissionsMap.entries.forEach { entry ->
- val permission = entry.key
- val isGranted = entry.value
- if (!isGranted) {
- // 关键判断:检查该权限是否被永久拒绝
- if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
- "有权限被永久拒绝".logE()
- permanentlyDeniedPermissions.add(permission)
- }
- }
- }
- }
- }
- val isShowRefusePermissionDialog = remember { mutableStateOf(true) }
- // 如果有权限被永久拒绝,可以在这里提示用户前往应用设置页手动开启
- if (permanentlyDeniedPermissions.isNotEmpty() && isShowRefusePermissionDialog.value) {
- // 例如,显示一个对话框引导用户去设置
- Dialog(onDismissRequest = {
- isShowRefusePermissionDialog.value = false
- refuse.invoke()
- }) {
- // 自定义对话框内容
- Card(
- colors = CardDefaults.cardColors(
- containerColor = Color.White, // 默认状态
- disabledContainerColor = Color.LightGray // 禁用状态
- )
- ) {
- Column(
- modifier = Modifier.padding(20.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(
- text = "温馨提示",
- fontSize = 18.sp,
- modifier = Modifier.padding(bottom = 18.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Text(
- text = "需要蓝牙权限来扫描和连接附近的设备,但是权限被拒绝,请前往设置页面手动开启",
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- isShowRefusePermissionDialog.value = false
- // 跳转到应用设置页面
- val intent = android.content.Intent(
- android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- android.net.Uri.parse("package:${context.packageName}")
- )
- context.startActivity(intent)
- }) {
- Text("知道了", color = MaterialTheme.colorScheme.onPrimary)
- }
- }
- }
- }
- }
- return
- }
- // 检查是否已拥有所有权限
- val hasAllPermissions = remember(bluetoothPermissions) {
- bluetoothPermissions.all { permission ->
- ContextCompat.checkSelfPermission(
- context,
- permission
- ) == PackageManager.PERMISSION_GRANTED
- }
- }
- val isShowDialog = remember { mutableStateOf(true) }
- if (hasAllPermissions || allPermissionsGranted.value) {
- "所有权限都已授予".logE()
- agree.invoke()
- } else {
- if (isShowDialog.value) {
- Dialog(onDismissRequest = {
- isShowDialog.value = false
- refuse.invoke()
- }) {
- // 自定义对话框内容
- Card(
- colors = CardDefaults.cardColors(
- containerColor = Color.White, // 默认状态
- disabledContainerColor = Color.LightGray // 禁用状态
- )
- ) {
- Column(
- modifier = Modifier.padding(20.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(
- text = "申请权限",
- fontSize = 18.sp,
- modifier = Modifier.padding(bottom = 18.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Text(
- text = "需要蓝牙权限来扫描和连接附近的设备",
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(
- modifier = Modifier.weight(1f),
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.Gray.copy(alpha = 0.5f),
- contentColor = Color.White
- ),
- onClick = {
- isShowDialog.value = false
- refuse.invoke()
- }) {
- Text("取消")
- }
- Spacer(modifier = Modifier.width(16.dp))
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- permissionLauncher.launch(bluetoothPermissions)
- }) {
- Text("申请", color = MaterialTheme.colorScheme.onPrimary)
- }
- }
- }
- }
- }
- }
- }
- }
复制代码 申请相机权限
- 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.activity.compose.rememberLauncherForActivityResult
- import androidx.activity.result.contract.ActivityResultContracts
- import androidx.compose.foundation.layout.Arrangement
- import androidx.compose.foundation.layout.Column
- import androidx.compose.foundation.layout.Row
- import androidx.compose.foundation.layout.Spacer
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.height
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.width
- import androidx.compose.material3.Button
- import androidx.compose.material3.ButtonDefaults
- import androidx.compose.material3.Card
- import androidx.compose.material3.CardDefaults
- import androidx.compose.material3.MaterialTheme
- import androidx.compose.material3.Text
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.mutableStateListOf
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.unit.dp
- import androidx.compose.ui.unit.sp
- import androidx.compose.ui.window.Dialog
- import androidx.core.app.ActivityCompat
- import androidx.core.content.ContextCompat
- import com.yak.bleSdk.log.logE
- import com.yak.bleSdk.log.logI
- private val cameraPermissions by lazy {
- arrayOf(Manifest.permission.CAMERA)
- }
- /**
- * 检查相机权限
- */
- fun checkCameraPermissions(context: Context): Boolean {
- // 检查是否已拥有所有权限
- val hasAllPermissions = cameraPermissions.all { permission ->
- ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
- }
- return hasAllPermissions
- }
- /**
- * 请求相机权限
- * @param isRequest 是否请求权限, 这个布尔值可以用来控制申请权限的时机比如按下某个按钮后申请权限,如果不需要选择时机,可以默认为true
- * 如果需要控制申请时机,请参考:
- * ```kotlin
- * val isRequestCameraPermissions = remember { mutableStateOf(false) }
- * RequestCameraPermissions(
- * isRequest = isRequestCameraPermissions.value,
- * agree = {
- * // 用户同意权限后的操作
- * },
- * refuse = {
- * // 用户拒绝权限后的操作
- * }
- * )
- * Button(
- * onClick = {
- * if(!checkCameraPermissions(context)){
- * isRequestCameraPermissions.value = true
- * }
- * },
- * ) {
- * Text("点击申请权限", modifier = Modifier.padding(8.dp))
- * }
- * ```
- * @param agree 同意
- * @param refuse 拒绝
- */
- @Composable
- fun RequestCameraPermissions(isRequest: Boolean = true, agree: () -> Unit, refuse: () -> Unit) {
- if (!isRequest) {
- return
- }
- val context = LocalContext.current
- val activity = LocalContext.current as Activity
- val allPermissionsGranted = remember { mutableStateOf(false) }
- // 记录被永久拒绝的权限列表
- val permanentlyDeniedPermissions = remember { mutableStateListOf<String>() }
- val permissionLauncher = rememberLauncherForActivityResult(
- ActivityResultContracts.RequestMultiplePermissions()
- ) { permissionsMap ->
- allPermissionsGranted.value = permissionsMap.values.all { it }
- if (allPermissionsGranted.value) {
- // 所有权限都已授予
- "相机权限已授予".logI()
- agree.invoke()
- } else {
- // 处理权限被拒绝的情况
- "相机权限被拒绝".logE()
- refuse.invoke()
- // 遍历结果,找出被拒绝的权限,并判断是否为永久拒绝
- permissionsMap.entries.forEach { entry ->
- val permission = entry.key
- val isGranted = entry.value
- if (!isGranted) {
- // 关键判断:检查该权限是否被永久拒绝
- if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
- "有权限被永久拒绝".logE()
- permanentlyDeniedPermissions.add(permission)
- }
- }
- }
- }
- }
- val isShowRefusePermissionDialog = remember { mutableStateOf(true) }
- // 如果有权限被永久拒绝,可以在这里提示用户前往应用设置页手动开启
- if (permanentlyDeniedPermissions.isNotEmpty() && isShowRefusePermissionDialog.value) {
- // 例如,显示一个对话框引导用户去设置
- Dialog(onDismissRequest = {
- isShowRefusePermissionDialog.value = false
- refuse.invoke()
- }) {
- // 自定义对话框内容
- Card(
- colors = CardDefaults.cardColors(
- containerColor = Color.White, // 默认状态
- disabledContainerColor = Color.LightGray // 禁用状态
- )
- ) {
- Column(
- modifier = Modifier.padding(20.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(
- text = "温馨提示",
- fontSize = 18.sp,
- modifier = Modifier.padding(bottom = 18.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Text(
- text = "需要相机权限来扫描二维码,但是权限被拒绝,请前往设置页面手动开启",
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- isShowRefusePermissionDialog.value = false
- // 跳转到应用设置页面
- val intent = Intent(
- Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.parse("package:${context.packageName}")
- )
- context.startActivity(intent)
- }) {
- Text("知道了", color = MaterialTheme.colorScheme.onPrimary)
- }
- }
- }
- }
- }
- return
- }
- // 检查是否已拥有所有权限
- val hasAllPermissions = remember(cameraPermissions) {
- cameraPermissions.all { permission ->
- ContextCompat.checkSelfPermission(
- context,
- permission
- ) == PackageManager.PERMISSION_GRANTED
- }
- }
- val isShowDialog = remember { mutableStateOf(true) }
- if (hasAllPermissions || allPermissionsGranted.value) {
- "相机权限已授予".logI()
- agree.invoke()
- } else {
- if (isShowDialog.value) {
- Dialog(onDismissRequest = {
- isShowDialog.value = false
- refuse.invoke()
- }) {
- // 自定义对话框内容
- Card(
- colors = CardDefaults.cardColors(
- containerColor = Color.White, // 默认状态
- disabledContainerColor = Color.LightGray // 福用状态
- )
- ) {
- Column(
- modifier = Modifier.padding(20.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(
- text = "申请权限",
- fontSize = 18.sp,
- modifier = Modifier.padding(bottom = 18.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Text(
- text = "需要相机权限来扫描二维码",
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(
- modifier = Modifier.weight(1f),
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.Gray.copy(alpha = 0.5f),
- contentColor = Color.White
- ),
- onClick = {
- isShowDialog.value = false
- refuse.invoke()
- }) {
- Text("取消")
- }
- Spacer(modifier = Modifier.width(16.dp))
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- permissionLauncher.launch(cameraPermissions)
- }) {
- Text("申请", color = MaterialTheme.colorScheme.onPrimary)
- }
- }
- }
- }
- }
- }
- }
- }
复制代码 请求打开蓝牙
- import android.bluetooth.BluetoothAdapter
- import android.content.Context
- import android.content.Intent
- import android.provider.Settings
- import androidx.compose.foundation.layout.Arrangement
- import androidx.compose.foundation.layout.Column
- import androidx.compose.foundation.layout.Row
- import androidx.compose.foundation.layout.Spacer
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.height
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.width
- import androidx.compose.material3.Button
- import androidx.compose.material3.ButtonDefaults
- import androidx.compose.material3.Card
- import androidx.compose.material3.CardDefaults
- import androidx.compose.material3.Text
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.unit.dp
- import androidx.compose.ui.unit.sp
- import androidx.compose.ui.window.Dialog
- /**
- * 检查蓝牙是否已开启
- */
- fun isBluetoothEnabled(): Boolean {
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
- return bluetoothAdapter?.isEnabled ?: false
- }
- /**
- * 请求开启蓝牙功能
- */
- @Composable
- fun RequestOpenBluetooth(isRequest: Boolean = true) {
- if (!isRequest) {
- return
- }
- val context = LocalContext.current
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
- val isBluetoothEnabled = remember { mutableStateOf(bluetoothAdapter?.isEnabled ?: false) }
- val isShowDialog = remember { mutableStateOf(true) }
- if (!isBluetoothEnabled.value) {
- if (isShowDialog.value) {
- Dialog(onDismissRequest = {
- isShowDialog.value = false
- }) {
- Card(
- colors = CardDefaults.cardColors(
- containerColor = Color.White,
- disabledContainerColor = Color.LightGray
- )
- ) {
- Column(
- modifier = Modifier.padding(20.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(
- text = "开启蓝牙",
- fontSize = 18.sp,
- modifier = Modifier.padding(bottom = 18.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Text(
- text = "需要开启蓝牙来扫描和连接附近的设备",
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- Spacer(modifier = Modifier.height(10.dp))
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(
- modifier = Modifier.weight(1f),
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.Gray.copy(alpha = 0.5f),
- contentColor = Color.White
- ),
- onClick = {
- isShowDialog.value = false
- }) {
- Text("取消")
- }
- Spacer(modifier = Modifier.width(16.dp))
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- openBluetoothSettings(context)
- isShowDialog.value = false
- }) {
- Text("去设置")
- }
- }
- }
- }
- }
- }
- }
- }
- /**
- * 跳转到蓝牙设置页面
- */
- fun openBluetoothSettings(context: Context) {
- val intent = Intent(Settings.ACTION_BLUETOOTH_SETTINGS)
- context.startActivity(intent)
- }
复制代码
End
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |