일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Android
- androidstudio
- bitmap
- BOJ
- Canvas
- CS
- Database
- DBeaver
- DP
- Ecilpse
- Eclipse
- firebase
- git
- github
- GooglePlayServices
- gradle
- IDE
- IntelliJ
- java
- json
- kotlin
- level2
- linux
- mariadb
- MYSQL
- Paint
- permission
- python
- Sorting
- sourcetree
will come true
[Android/Kotlin] 퍼미션(Permission) 권한 확인 및 설정하기 본문
퍼미션(Permission)
- 안드로이드 앱의 특정 기능에 부여하는 접근 권한 (ex: 카메라 촬영 퍼미션, 전화 발신 퍼미션 등)
- 내가 개발하는 앱이 다른 앱이나 안드로이드 시스템에서 보호하는 특정 기능을 이용할 때 퍼미션 사용을 설정해야 한다. (다른 앱의 보호된 기능을 사용할 때)
- 또는 내가 만든 앱 기능을 다른 앱에서 사용할 수 있도록 보호하고 권한을 얻은 앱에서만 허용하고 싶을 때 퍼미션을 설정한다. (내 앱의 기능을 다른앱으로부터 보호할 때)
- 즉, 앱끼리 서로 연동하고 상호작용할 때 필요한 절차이다.
- 예를 들어, 카X카오톡과 같은 메신저 앱을 만들고 친구 프로필을 누르면 전화번호가 표시되면 내가 만든 앱에서도 [전화걸기] 버튼만 누르면 바로 친구에게 전화가 걸리게 하고 싶다면 안드로이드 시스템으로부터 '전화 발신' 기능에 대한 퍼미션을 얻어야 한다.(앱에서 퍼미션을 요청하는 순간 아래 사진처럼 '{앱 이름}에서 ~기능을 이용하도록 허용하시겠습니까?' 메시지가 뜨게되고, 여기서 '허용'을 클릭하면 퍼미션을 허용하는 게 된다.) 물론 퍼미션은 '이 기기에서 해당 앱이 특정 기능을 사용하는 걸 허가한다'는 허용 행위일 뿐이기 때문에, 실제로 전화걸기 기능을 구현하기 위해서는 전화 인텐트(ACTION_CALL)를 실행하는 코드를 따로 작성해야 '전화걸기' 라는 기능을 구현할 수 있다.
- 안드로이드 기기에서 [설정 > 앱 정보 > 앱 권한]에 접속하면 각 앱에 설정된 권한을 확인할 수 있다. 여기서 [앱 권한]에 표시되는 퍼미션들이 개발자가 Manifest.xml 파일에 <uses-permission> 태그로 기재해둔 퍼미션 목록이다.
1. 퍼미션 사용 설정
이 앱이 어떤 퍼미션을 요청·사용할지 그 목록을 미리 매니페스트(Manifest)에 설정한다. <uses-permission> 태그의 name 속성에 퍼미션 종류를 기재한다. 요청할 퍼미션 개수대로 <uses-permission> 태그를 추가한다.
*퍼미션 종류 : https://developer.android.com/reference/android/Manifest.permission
API 레벨 23 이전에는 이와같이 개발자가 매니페스트 파일에 <uses-permission>으로 선언만 하면 바로 보호받는 기능을 앱에서 이용할 수 있었지만(=이러한 기능을 사용하겠다는 통보식), API 레벨 23 버전부터는 퍼미션이 '허가제'로 바뀌었기 때문에 사용자로부터 직접 '허가'를 받아야만 특정 기능을 이용할 수 있다. 즉, API 레벨이 22(롤리팝)이던 2014년만 해도 "이 앱이 기기의 위치 정보에 접근하는 걸 허용하시겠습니까? 거부 / 허용" 과 같은 메시지 창이 안떴으나 어느 순간부터 위와같은 퍼미션 요청창이 일상적으로 뜨는 걸 볼 수 있다.
2. 퍼미션 허용 확인
사용하려는 권한을 이미 부여받았는지 확인한다. 사용자가 이전에 한번이라도 퍼미션 요청을 허가했으면 이후로는 퍼미션 요청 메시지가 뜨지 않도록 해야한다. 참고로 앱을 설치한 후 초기 퍼미션은 모두 거부 상태이기 때문에 필요한 권한을 허용해 달라고 사용자에게 요청해야 한다.
- 이전에 퍼미션을 허용했을 경우 → 앱 기능 실행
- 이전에 퍼미션을 거부했을 경우 → 퍼미션 허용 재요청 + 필요 이유 설명
- 최초로 퍼미션 요청을 받았을 경우 → 퍼미션 허용 요청
이번 예제에서는 메인화면에 버튼을 하나 배치시키고, 이 버튼을 누르면 '카메라(CAMERA)' 권한을 요청하도록 한다.
package com.example.myapplication
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission)
// 2. 퍼미션 허용 확인
btnCameraPermission.setOnClickListener {
val status = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (status == PackageManager.PERMISSION_GRANTED) {
// 퍼미션이 허용되어 있는 경우
Toast.makeText(this, "Permission granted for camera.", Toast.LENGTH_LONG).show()
} else {
// 퍼미션이 거부되어 있는 경우 (default)
Toast.makeText(this, "Permission denied for camera.", Toast.LENGTH_LONG).show()
}
}
}
}
※ 이때 카메라 퍼미션 문자열 상수를 가지고 있는 Manifest 클래스를 참조하게 되는데, 'android.Manifest'를 Import해야 한다. 같은 이름의 'java.util.jar.Manifest'가 Import 되지 않도록 주의하자.
ContextCompat.checkSelfPermission(context: Context, permission: String)
앱이 이미 특정 기능에 대한 권한을 부여받았는지 확인한다.
Parameters | |
permission | 앱에서 요청하길 원하는 퍼미션 종류 |
Return | |
int | PackageManager.PERMISSION_GRANTED - 권한을 허용한 경우 PackageManager.PERMISSION_DENIED - 권한을 거부한 경우 |
shouldShowRequestPermissionRationale(activity: Activity, permission: String)
사용자가 이전에 퍼미션 요청을 거부한 경우 true를 반환한다. 이 메서드의 반환값으로 사용자가 이전에 퍼미션 요청을 받았으나 거부한 경우 / 퍼미션 요청을 최초로 받은 경우를 구분하여 처리할 수 있다. (ex: 각 경우에 따라 다른 내용의 다이얼로그 표시하기)
Parameters | |
activity | 대상 액티비티 |
permission | 앱에서 허용을 요청하는 퍼미션 이름 *Manifest.permission 에 정의된 문자열 상수를 사용한다. |
Return | |
boolean | 퍼미션 요청 이유를 표시해야하는지 여부 |
3. 퍼미션 허용 요청
앱을 최초 실행했거나, 이전에 퍼미션을 거부한 경우 퍼미션 허용을 요청하는 메시지창을 표시한다. String 배열을 사용해 한번에 여러 개의 퍼미션을 요청할 수 있다. requestPermissions() 메서드를 이용한 퍼미션 요청 결과(=사용자 응답)는 onRequestPermissionResult() 에서 받아 확인할 수 있다.
앞에서 호출한 checkSelfPermission() 메서드 결과값에 따라 퍼미션이 거부되어 있을 경우에만 권한을 요청한다.
package com.example.myapplication
import ...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission)
// 2. 퍼미션 허용 확인
btnCameraPermission.setOnClickListener {
val status = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (status == PackageManager.PERMISSION_GRANTED) {
// 퍼미션이 허용되어 있는 경우
Toast.makeText(this, "Permission granted for camera.", Toast.LENGTH_LONG).show()
} else {
// 퍼미션이 거부되어 있는 경우 (default)
Toast.makeText(this, "Permission denied for camera.", Toast.LENGTH_LONG).show()
// 3. 퍼미션 허용 요청
ActivityCompat.requestPermissions(
this,
arrayOf<String>(Manifest.permission.CAMERA),
100
)
}
}
}
}
requestPermissions(activity: Activity, permissions: Array<String>, requestCode: int)
사용자에게 퍼미션 허용을 요청한다. 이 함수가 호출되면 퍼미션을 요청하는 다이얼로그가 화면에 표시된다. 함수의 반환값은 따로 없고, 퍼미션 요청 결과는 onRequestPermissionResult() 함수에 전달된다.
Parameters | |
activity | 대상 액티비티 |
permission | 앱에서 허용을 요청하는 퍼미션 목록 (문자열 배열로 한번에 여러 개 요청 가능) |
requestCode | onRequestPermissionResult() 에서 구분용으로 사용할 요청 코드 |
4. 퍼미션 요청 결과 확인, 처리
퍼미션 허용 요청 메시지창에 대한 사용자의 반응인 퍼미션 허용 요청 결과를 확인한다.
- 퍼미션을 허용할 경우 → 앱 기능 실행
- 퍼미션을 허용하지 않을 경우 → 앱 기능 실행 안함 (해당 기능을 실행하기 위해선 반드시 퍼미션이 필요함)
package com.example.myapplication
import ...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission)
// 2. 퍼미션 허용 확인
btnCameraPermission.setOnClickListener {
val status = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (status == PackageManager.PERMISSION_GRANTED) {
// 퍼미션이 허용되어 있는 경우
Toast.makeText(this, "Permission granted for camera.", Toast.LENGTH_LONG).show()
} else {
// 퍼미션이 거부되어 있는 경우 (default)
Toast.makeText(this, "Permission denied for camera.", Toast.LENGTH_LONG).show()
// 3. 퍼미션 허용 요청
ActivityCompat.requestPermissions(
this,
arrayOf<String>(Manifest.permission.CAMERA),
100
)
}
}
}
// 4. 퍼미션 허용 요청 결과 확인
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 사용자가 퍼미션을 허용함
Toast.makeText(this, "Permission granted for camera.", Toast.LENGTH_LONG).show()
} else {
// 사용자가 퍼미션을 거부함
Toast.makeText(this, "Permission denied for camera.", Toast.LENGTH_LONG).show()
}
}
}
onRequestPermissionsResult(String[], int[])
이 함수를 선언해 놓으면 퍼미션 요청 다이얼로그가 닫힐 때 자동으로 호출되며, 사용자가 다이얼로그에서 퍼미션을 허용했는지 요청 결과를 grantResults 매개변수로 받는다.
Parameters | |
requestCode | requestPermissions() 메서드로 퍼미션 허용을 요청했을 때 사용자가 설정해둔 요청 코드 |
permissions | 앱에서 허용을 요청한 퍼미션 목록 |
grantResults | 다이얼로그에서 요청한 퍼미션의 결과값 |
+) ActivityResultLauncher를 사용한 퍼미션 요청 방법
[MainActivity.kt] *Manifest, main_activity 코드는 위 예제와 동일
package com.example.permissionsdemo
import android.Manifest
import android.app.AlertDialog
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
// 카메라 퍼미션 요청
private val cameraResultLauncher : ActivityResultLauncher<String> =
registerForActivityResult(ActivityResultContracts.RequestPermission()){ isGranted ->
if (isGranted) {
Toast.makeText(this, "Permission granted for camera.", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this, "Permission denied for camera.", Toast.LENGTH_LONG).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission)
btnCameraPermission.setOnClickListener {
// 카메라 퍼미션 확인
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
// API 23 이상이고, 이전에 퍼미션을 거부했을 경우
showRationaleDialog("Permission Demo requires camera access", "Camera cannot be used because Camera access is denied")
} else {
// API 23 미만이거나 최초로 퍼미션 요청을 받았을 경우
cameraResultLauncher.launch(Manifest.permission.CAMERA)
}
}
}
// 특정 퍼미션이 필요한 이유를 설명하는 다이얼로그, 사용자가 이전에 퍼미션 요청을 거부했을 경우에 표시
private fun showRationaleDialog(title: String, message:String) {
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
// 타이틀, 메시지, 버튼 1개 설정
builder.setTitle(title)
.setMessage(message)
.setPositiveButton("Cancel"){ dialog, _ ->
dialog.dismiss()
}
builder.create().show()
}
}
[참고자료]
- Android Developers - Permission, https://developer.android.com/guide/topics/permissions/overview