런타임 권한을 선언하고 요청하기 전에, 앱에서 해당 작업이 필요한 지 점검해야 합니다. 그렇게 해서 불필요한 권한 요청을 하지 않도록 합니다.
앱의 기능을 Gracefully degrade 한다고 합니다.
rememberPermissionStaterememberMultiplePermissionsState각각 한 개, 여러 개의 권한을 요청할 수 있는 권한 요청 API 입니다.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationPermissionScreen() {
val context = LocalContext.current
// 위치 권한 목록 정의
val locationPermissions = listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
// 여러 권한을 한번에 처리하기 위한 상태 관리
val multiplePermissionsState = rememberMultiplePermissionsState(
permissions = locationPermissions
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
when {
// 모든 권한이 허용된 경우
multiplePermissionsState.allPermissionsGranted -> {
Text("위치 권한이 허용되었습니다!")
}
// 권한이 거부된 경우 사용자에게 이유 설명
multiplePermissionsState.shouldShowRationale -> {
Text("위치 기능을 사용하기 위해서는 위치 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
multiplePermissionsState.launchMultiplePermissionRequest()
}) {
Text("권한 허용하기")
}
}
// 처음 권한 요청하는 경우
else -> {
Text("이 앱은 위치 기반 서비스를 제공하기 위해 위치 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
multiplePermissionsState.launchMultiplePermissionRequest()
}) {
Text("권한 요청하기")
}
}
}
}
}
rememberMultiplePermissionsState() 를 사용함.// 위치 권한 목록 정의
val locationPermissions = listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
// 여러 권한을 한번에 처리하기 위한 상태 관리
val multiplePermissionsState = rememberMultiplePermissionsState(
permissions = locationPermissions
)
승인된 경우
// 모든 권한이 허용된 경우
multiplePermissionsState.allPermissionsGranted -> {
Text("위치 권한이 허용되었습니다!")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
// 위치 정보 가져오기
getUserLocation(fusedLocationClient, context)
}) {
Text("현재 위치 가져오기")
}
}
권한이 거부된 경우
// 권한이 거부된 경우 사용자에게 이유 설명
multiplePermissionsState.shouldShowRationale -> {
Text("위치 기능을 사용하기 위해서는 위치 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
multiplePermissionsState.launchMultiplePermissionRequest()
}) {
Text("권한 허용하기")
}
}
처음 요청하는 경우
// 처음 권한 요청하는 경우
else -> {
Text("이 앱은 위치 기반 서비스를 제공하기 위해 위치 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
multiplePermissionsState.launchMultiplePermissionRequest()
}) {
Text("권한 요청하기")
}
}
<uses-permission android:name="android.permission.CAMERA" />
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraPermissionScreen() {
// 단일 권한 상태 관리
val cameraPermissionState = rememberPermissionState(
permission = Manifest.permission.CAMERA
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
when {
// 권한이 허용된 경우
cameraPermissionState.status.isGranted -> {
Text("카메라 권한이 허용되었습니다.")
}
// 권한이 거부된 경우 사용자에게 이유 설명
cameraPermissionState.status.shouldShowRationale -> {
Text("카메라 기능을 사용하기 위해서는 카메라 권한이 필요합니다. 권한을 허용해주세요.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("권한 허용하기")
}
}
// 처음 권한 요청하는 경우
else -> {
Text("이 앱은 카메라 기능을 제공하기 위해 카메라 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("권한 요청하기")
}
}
}
}
}
rememberPermissionState() 사용// 단일 권한 상태 관리
val cameraPermissionState = rememberPermissionState(
permission = Manifest.permission.CAMERA
)
승인된 경우
// 권한이 허용된 경우
cameraPermissionState.status.isGranted -> {
Text("카메라 권한이 허용되었습니다!")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
// 카메라 기능 실행 코드
}) {
Text("카메라 열기")
}
}
권한이 거부된 경우
// 권한이 거부된 경우 사용자에게 이유 설명
cameraPermissionState.status.shouldShowRationale -> {
Text("카메라 기능을 사용하기 위해서는 카메라 권한이 필요합니다. 권한을 허용해주세요.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("권한 허용하기")
}
}
처음 요청하는 경우
// 처음 권한 요청하는 경우
else -> {
Text("이 앱은 카메라 기능을 제공하기 위해 카메라 권한이 필요합니다.")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("권한 요청하기")
}
}
사용자가 처음 권한을 거부한 경우와 2회 이상 거부한 겨우를 구분해야 합니다.
처음 거부한 경우에는 shouldShowRationale() 을 통해서 권한이 필요한 이유를 설명하고 다시 서비스 내에서 권한 허용 팝업을 호출할 수 있습니다.
하지만 그 이상 거부했을 경우(shouldShowRationale() 을 실행했음에도 불구하고 거부했을 경우), 사용자를 앱 설정으로 이동시켜 수동으로 권한을 허용하도록 안내해야 합니다.
var showRationale by remember { mutableStateOf(false) }
// 최초 요청이거나 , shouldShowRationale 도 거절한 경우
if(!multiplePermissionsState.allPermissionsGranted &&
!multiplePermissionsState.shouldShowRationale) {
// 2번 이상 거절
if (showRationale) {
Text("[2회 거절] 권한이 필요합니다. 앱 설정에서 권한을 허용해주세요.")
Button(onClick = {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
context.startActivity(intent)
}) {
Text("설정으로 이동")
}
// 최초 요청
} else {
Text("최초 요청")
Button(onClick = {
multiplePermissionsState.launchMultiplePermissionRequest()
showRationale = true
}) {
Text("권한 요청하기")
}
}
}