📌 Intro
android 6.0 마시멜로부터 런타임 권한 처리가 추가되었다. 예를 들어서 카메라와 갤러리를 호출하기 위해서는 접근 권한이 필요한데, 6.0 이전까지는 AndroidManifest에 사용할 permission만 추가하면 되었지만 이제는 앱 사용 중 실제로 접근하는 시점에 권한을 허용했는지 체크하여 처리를 해야한다. 이 방법에 대해 알아보도록 하자.
📌 AndroidManifest.xml 에 퍼미션 추가
가장 먼저 AndroidManifest.xml 파일에 퍼미션을 추가해줘야 한다.
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>
📌 권한 요청
private val CAMERA_REQUESTCODE : Int = 100 // 카메라 권한 확인코드
private val permissions = arrayOf(Manifest.permission.CAMERA) // 허용받을 권한 리스트
private fun checkPermission() : Boolean{
return (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// viewBinding
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if(checkPermission()){ // 권한이 있는 경우
// 원하는 작업 진행
}
else{ // 권한이 없는 경우
requestPermissions(permissions, CAMERA_REQUESTCODE) // 권한 요청
}
}
사용자가 이미 앱에 특정 권한을 부여했는지 확인하려면 ContextCompat.checkSelfPermission() 메서드에 권한을 전달하면 된다. 이 메서드는 앱에 권한이 있는지에 따라 PERMISSION_GRANTED 또는 PERMISSION_DENIED를 반환한다. 이를 바탕으로 checkPermission()을 작성했다. checkPermission()은 현재 어플리케이션에서 내가 필요한 권한을 부여 받았는지를 확인한다.
requestPermissions() 함수는 사용자에게 명시적으로 권한을 요청하고, 안드로이드 시스템 표준 대화상자를 사용하며 이는 변경할 수 없다.
requestPermissions() 함수는 인자로 String[] permissions, int requestCode를 받는다. 따라서 허용받을 권한들을 리스트 형태 permissions에 저장하고, 어떤 권한을 요청했는지 확인하기 위한 CAMERA_REQUESTCODE를 선언한 것이다.
public final void requestPermissions(@android.annotation.NonNull java.lang.String[] permissions, int requestCode) { /* compiled code */ }
requestPermission()을 통해 사용자에게 퍼미션을 요청하면 위와 같은 팝업이 뜬다. 사용자가 허용 또는 거부를 누르면 그 결과가 앱으로 전달되는데 이 때 onRequestPermissionsResult()를 호출한다.
📌 권한 요청처리 결과 수신
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == CAMERA_REQUESTCODE && grantResults[0] == PackageManager.PERMISSION_GRANTED){ // 1
Toast.makeText(this, "권한 허용 O", Toast.LENGTH_SHORT).show()
}
else{ // 권한이 없는 경우
if(shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)){ // 2
requestPermissions(permissions, CAMERA_REQUESTCODE)
}
else{
showDialogToGetPermission() // 3
}
}
}
private fun showDialogToGetPermission(){
val builder = AlertDialog.Builder(this)
builder.setTitle("권한 요청")
.setMessage("카메라 촬영 권한이 필요합니다. " +
"권한을 허용하기 위해서 설정으로 이동해야 합니다.")
// 확인 버튼
builder.setPositiveButton("확인") { dialogInterface, i ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
// 취소 버튼
builder.setNegativeButton("취소") { dialogInterface, i ->
// ignore
}
val dialog = builder.create()
dialog.show()
}
- grantResults에는 각 퍼미션에 대한 요청 결과가 담겨져있다. 여러개의 퍼미션을 요청할 수 있기 때문에 결과도 배열로 전달된다. 사용자가 허용했다면 PackageManager.PERMISSION_GRANTED을, 거부했다면 PackageManager.PERMISSION_DENIED를 담고 있다. 만약 요청한 requestCode와 내가 확인하려는 퍼미션의 code가 같다면 grantResults에 담긴 값으로 퍼미션의 허용유무를 확인하면 된다.
- shouldShowRequestPermissionRationale()는 사용자가 거부 버튼을 클릭했을 때 true를 리턴한다. 그리고 "다시 묻지 않음"을 선택하고 거부를 클릭하면 false를 리턴한다. true를 리턴하는 경우 사용자에게 다시 퍼미션을 요청하도록 할 수 있다.
- false가 리턴되는 경우 requestPermissions를 호출해도 요청 팝업이 뜨지 않는다. 퍼미션을 받으려면 사용자가 직접 Settings의 내 앱 정보에 들어가서 퍼미션을 변경해야 한다. 따라서, 왜 퍼미션이 필요한지 설명하는 팝업을 띄우고 OK 버튼을 누르면 Settings로 이동할 수 있도록 구현했다.
📌 결과 화면
사용자에게 첫 퍼미션 요청
사용자가 거부를 클릭한 경우 shouldShowRequestPermissionRationale()에서 true리턴 → 퍼미션을 다시 요청하도록 함
사용자가 "다시 묻지 않음"을 클릭하고 거부한 경우
퍼미션이 필요한 이유를 설명하는 팝업창이 뜨도록 함. 확인 버튼을 클릭하면 Settings으로 바로 이동하도록 함.
사용자가 직접 퍼미션을 줄 수 있도록 함.
참고
[1] https://shary1012.tistory.com/243
[2] https://greedy0110.tistory.com/59
[3] https://codechacha.com/ko/android-request-runtime-permissions/
[4] https://developer.android.com/training/permissions/requesting?hl=ko