-
컴포즈 블루투스 권한 없을 때 처리질문 정리 2024. 6. 27. 16:09
문제 상황
현재 블루투스 권한이 없을 때, 스캔을 시도하면 버튼을 눌러도 사용자에게 피드백이 없습니다.
요구 사항
해당 기능을 사용하려고 할 때, 권한 체크를 수행하고 권한이 없을 경우, 다음의 조치를 취해야 합니다.
- 이전에 권한 요청을 했다면
- 권한이 필요한 이유를 팝업으로 표시
- 설정으로 이동할 수 있는 버튼 제공
- 이번이 권한 요청이 처음이면
- 바로 권한을 요청
블루투스 통신을 위해 필요한 절차
- 휴대폰 블루투스가 켜져 있는지 확인하기
- 앱에서 블루투스 관련 권한 받기
- 스캔하기
- 기기 연결하기
블루투스 상태 모델
앱에서 블루투스 상태관리를 위하여 다음의 모델을 사용했습니다.
- DISABLED: 사용자에게 블루투스를 활성화할 것을 요청합니다.
- PERMISSION_DENIED: 사용자가 필요한 권한을 부여하지 않았을 때, 설정으로 유도하여 권한을 요청합니다.
- READY: 블루투스와 모든 권한이 준비되어 있어서 바로 장치 검색이나 연결을 시작할 수 있는 상태입니다.
- SEARCHING: 장치 검색을 진행 중이며, 검색 중에는 이 상태를 유지합니다.
- CONNECTED: 특정 장치에 연결이 완료되어 데이터 교환을 할 수 있는 상태입니다.
- ERROR: 기대하지 않은 문제가 발생했을 때 이 상태로 전환하여 오류 처리 로직을 수행할 수 있습니다.
코드 설명
1. 휴대폰 블루투스 기능 활성화
휴대폰의 블루투스 기능이 켜져 있는지 확인합니다. 블루투스가 꺼져 있다면 사용자가 이를 활성화하도록 요청하는 창을 띄웁니다.
@Composable fun SetupHomeStateEffects( bluetoothAdapter: BluetoothAdapter?, bluetoothEnableLauncher: ActivityResultLauncher<Intent>, permissionLauncher: ActivityResultLauncher<Array<String>> ) { val context = LocalContext.current LaunchedEffect(bluetoothAdapter) { // Bluetooth 활성화 요청 bluetoothAdapter?.let { if (!it.isEnabled) { val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) bluetoothEnableLauncher.launch(enableBtIntent) } } } LaunchedEffect(Unit) { // 블루투스 사용 권한 handleBlePermission(permissionLauncher, context) } }
2. 블루투스 설정 화면으로 이동
사용자가 블루투스를 활성화하지 않고 원래 화면으로 돌아왔을 경우, 블루투스 설정 화면으로 이동시킵니다.
@Composable fun RememberBluetoothEnableLauncher( context: Context, ): ActivityResultLauncher<Intent> = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode != Activity.RESULT_OK) { context.startActivity(Intent(Settings.ACTION_BLUETOOTH_SETTINGS)) } }
3. 블루투스 관련 권한 받기
각 버전에 필요한 권한 리스트를 구성하여 요청합니다.
private fun buildPermissionsList(): MutableList<String> { val permissions = mutableListOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { permissions.addAll( listOf( Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN ) ) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { permissions.addAll( listOf( Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN ) ) } return permissions } private fun handleBlePermission( permissionLauncher: ActivityResultLauncher<Array<String>>, context: Context ) { // 버전에 따른 권한 리스트 생성 val permissions = buildPermissionsList() permissionLauncher.launch(permissions.toTypedArray()) }
4. 설명 다이얼로그
사용자가 이전에 권한 요청을 거절한 적이 있다면, 권한이 필요한 이유를 설명하는 다이얼로그를 띄웁니다. 이를 통해 사용자가 권한의 필요성을 이해하고 설정으로 이동할 수 있도록 합니다.
fun handleBlePermissionDialog( permissionLauncher: ActivityResultLauncher<Array<String>>, homeViewModel: HomeViewModel, context: Context ) { if (ActivityCompat.shouldShowRequestPermissionRationale( context as Activity, Manifest.permission.ACCESS_COARSE_LOCATION ) ) { // 권한을 요청에 대한 추가적인 설명이 필요한 경우 homeViewModel.updateBlePermissionChange(true) } else { // 그냥 권한을 요청하는 경우 handleBlePermission(permissionLauncher, context) } }
@Composable fun HandleBlePermissionDialog(homeViewModel: HomeViewModel) { val context = LocalContext.current if (homeViewModel.showBlePermissionDialog.value) { ConfirmationDialog( title = stringResource(R.string.bluetooth_permission_title), body = stringResource(R.string.bluetooth_permission_explanation), acceptBtnTitle = stringResource(R.string.change_now), declineBtnTitle = stringResource(R.string.change_later), onDismissRequest = { homeViewModel.updateBlePermissionChange(false) }, onDeclineClick = { homeViewModel.updateBlePermissionChange(false) } ) { homeViewModel.updateBlePermissionChange(false) // 설명 확인 후 버튼 누르면, 설정 화면으로 이동 navigateToSettings(context) } } }
5. 사용 전 권한 확인하기
BLE 통신을 하는 경우, 권한 상태를 확인하고 없다면 요청합니다.
private fun handleDeviceScanClick( bluetoothAdapter: BluetoothAdapter?, homeViewModel: HomeViewModel, permissionLauncher: ActivityResultLauncher<Array<String>>, context: Context ) { if (bluetoothAdapter != null) { val blePermissionResult = homeViewModel.blePermissionResult.value if (bluetoothAdapter.isEnabled && blePermissionResult == BLESTATES.READY) { // 블루투스 스캔 } else if (blePermissionResult == BLESTATES.PERMISSION_DENIED) { HandleBlePermissionDialog(permissionLauncher, homeViewModel, context) } } } @Composable fun RememberPermissionLauncher( homeViewModel: HomeViewModel ): ActivityResultLauncher<Array<String>> = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> val allPermissionsGranted = permissions.entries.all { it.value } homeViewModel.updateBlePermissionResult( if (allPermissionsGranted) BLESTATES.PERMISSION_GRANTED else BLESTATES.PERMISSION_DENIED ) }
6. 홈 화면
위의 코드들을 사용하는 방법입니다.
@Composable fun HomeScreenContent( navController: NavController, homeViewModel: HomeViewModel, ) { val context = LocalContext.current val homeState by homeViewModel.homeViewState.collectAsState() val bluetoothAdapter = RememberBluetoothAdapter(context) // Initialize and prepare all launchers // 블루투스 온오프 런처 val bluetoothEnableLauncher = RememberBluetoothEnableLauncher(context) // 퍼미션 관련 런처 val permissionLauncher = RememberPermissionLauncher(homeViewModel) // Reactive effects for state updates SetupHomeStateEffects( homeViewModel, bluetoothAdapter, bluetoothEnableLauncher, permissionLauncher ) Column { HomeHeader(homeState) HomeContent(navController, homeViewModel, bluetoothAdapter, homeState, permissionLauncher) } HandleBlePermissionDialog(homeViewModel) }
참고링크
Bluetooth permissions | Connectivity | Android Developers
블루투스 권한 | Connectivity | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 블루투스 권한 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱에서 블루투스 기능을 사용하려면 여
developer.android.com
https://developer.android.com/develop/connectivity/bluetooth/ble/ble-overview
블루투스 저전력 | Connectivity | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 블루투스 저전력 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android는 중심 역할로 저전력 블루투
developer.android.com
'질문 정리' 카테고리의 다른 글
안드로이드에서 CRC32로 무결성 체크하기 (0) 2024.07.02 안드로이드 PDF 파일 저장하기 구현 (1) 2024.06.28 Compose Snackbar 알림 관리를 위한 Utils (0) 2024.06.20 Intent 정리: 문의하기 클릭 시 이메일 앱으로만 연결 (0) 2024.06.19 Kotlin에서 리스트와 맵의 변환: 순서가 유지될까? (0) 2024.06.18 - 이전에 권한 요청을 했다면