오늘도 더 나은 코드를 작성하였습니까?

앱에 권한 요청하기! 본문

Android Basic

앱에 권한 요청하기!

hik14 2021. 3. 18. 17:09

모든 Android 앱은 액세스가 제한된 샌드박스에서 실행됩니다.

앱이 자체 샌드박스 밖에 있는 리소스나 정보를 사용해야 하는 경우 권한을 선언하고 이 액세스를 제공하는 권한 요청을 설정할 수 있습니다.

기본 원칙

런타임 권한을 요청하기 위한 기본 원칙은 다음과 같습니다.

 

- 사용자가 권한이 필요한 기능과 상호작용하기 시작할 때 컨텍스트에 따라 권한을 요청합니다.

 

- 사용자를 차단하지 않습니다. 권한과 관련된 교육용 UI 흐름을 취소하는 옵션(dialog 등으로 권한이 왜 필요한지 설명 및 재요청)을 제공

 

- 사용자가 기능에 필요한 권한을 거부하거나 취소하면 권한이 필요한 기능을 사용 중지하는 등의 방법으로 앱의 성능을 단계적으로 저하시켜 사용자가 앱을 계속 사용할 수 있도록 합니다.(즉, 특정 기능에 관련된 권한을 거부 했다고 앱자체를 못쓰게 하면 안되는것이다.)

 

- 시스템 동작을 가정하지 않습니다. 예를 들어 동일한 권한 그룹에 권한이 표시된다고 가정하면 안된다. 

 권한 그룹은 앱이 밀접하게 관련된 권한을 요청할 때 시 사용자에게 표시하는 시스템 대화상자의 수를 최소화하는 데만 도움이 됩니다.

 

즉, 아래 보면 런타임권한(위험권한)이 각각의 그룹에 묶여 있는데 각각 따로 선언하고 받아야지 충분한 설명을 하고 받아야 한다.

그룹내부에 3개를 선언하고 요청 대화상자가 1개에 1개만 설명하고 권한을 받아서는 안된다. 

권한 요청 워크 플로우

 

앱의 매니페스트 파일에서 앱이 요청할 필요가 있을 권한을 선언합니다.

 

앱의 특정 작업이 알맞은 런타임 권한과 연결되도록 앱의 UX를 설계합니다.

(사용자는 앱에 비공개 사용자 데이터에 대한 액세스 권한을 부여해야 할 작업을 알아야 합니다)

 

특정 비공개 사용자 데이터에 액세스해야 하는 앱의 작업을 사용자가 호출할 때까지 기다립니다. 이때 앱은 데이터에 액세스하는 데 필요한 런타임 권한을 요청한다.

 

사용자가 이미 앱에 필요한 런타임 권한을 부여했는지 확인합니다. 부여했다면 앱에서 비공개 사용자 데이터에 액세스할 수 있습니다. 사용자가 부여하지 않았다면 다음 단계로 이동합니다

 

런타임 권한이 필요한 작업을 실행할 때마다 권한이 있는지 확인해야 합니다.

 

사용자에게 앱에서 권한이 필요한 이유를 알려준다.

시스템에서 앱이 근거를 표시하지 않아야 한다고 판단(일반권한- 인터넷 등등)하면 UI 요소를 표시하지 않고 다음 단계로 바로 진행합니다.

 

시스템에서 앱이 이유를 표시해야 한다고 판단하면 사용자에게 이유를 UI 요소로 표시합니다.

UI를 통해 앱이 액세스하려는 데이터가 무엇인지, 런타임 권한을 부여하면 앱이 사용자에게 제공할 수 있는 이점이 무엇인지 명확하게 설명해야 합니다.

 

앱에서 비공개 사용자 데이터에 액세스하기 위해 필요한 런타임 권한을 요청합니다. 

권한 개요 페이지에 표시된 것과 같은 런타임 권한 메시지가 시스템에 표시됩니다.

 

런타임 권한 부여를 선택했는지 또는 거부를 선택했는지 사용자의 응답을 확인합니다.

 

권한 허용시 -  비공개 사용자 데이터에 액세스할 수 있습니다.

권한 거부시 - 권한으로 보호되는 정보 없이도 사용자에게 기능을 제공하도록 앱 환경의 성능을 단계적으로 저하합니다.

 

요청하는 순서

1. 앱에 이미 권한이 부여되었는지 확인

 

- ContextCompat.checkSelfPermission() 메서드에 권한을 전달합니다.

반환 값

PERMISSION_GRANTED (허용된 상태)

PERMISSION_DENIED (거부된 상태)

 

2. 앱에 권한이 필요한 이유 설명

 

- ContextCompat.checkSelfPermission() 메서드가 PERMISSION_DENIED를 반환되면,

 shouldShowRequestPermissionRationale() (사용자에게 권한을 요청하는 이유를 보여줄것인가 아닌가) 호출하세요. 

위 메서드가 true를 반환하면 교육용 UI를 사용자에게 표시합니다. 이때 UI에서 사용자가 사용 설정하려는 기능에 특정 권한이 필요한 이유를 설명하면되다.

 

3. 권한 요청

 

미리 사용자에게 교육용 UI가 표시되거나 shouldShowRequestPermissionRationale()의 반환 값에서 이번에는 교육용 UI를 표시하지 않아도 된다고 나타내면 그냥 바로 권한을 요청합니다.

 

사용자에게 시스템 권한 대화상자가 표시되고 사용자는 여기서 특정 권한을 앱에 부여할지 선택할 수 있습니다.

 

일반적으로 권한 요청의 일부로 요청 코드를 직접 관리하고 이 요청 코드를 권한 콜백 로직에 포함합니다.

또 다른 옵션은 AndroidX 라이브러리에 포함된 RequestPermission 계약을 사용하는 것으로 여기서 시스템이 권한 요청 코드를 관리하도록 허용합니다. RequestPermission 계약을 사용하면 로직이 간소화되므로 가능하면 사용하는 것이 좋습니다.(어지간하면 이거 쓰자)

 

시스템이 권한 요청 코드를 관리하도록 하기

시스템이 권한 요청과 연결된 요청 코드를 관리하도록 허용하려면

모듈수준의 build.gradle 파일에 androidx.activity 라이브러리의 종속 항목을 추가합니다. (버전 1.2.0 이상의 라이브러리를 사용)

 

단일 권한을 요청 -  RequestPermission

여러 권한을 동시에 요청 -  RequestMultiplePermissions

 

다음 단계는 RequestPermission 계약을 사용하는 방법을 보여줍니다.  RequestMultiplePermissions 도 거의 같다.

 

 

- activity 또는 fragment의 초기화 로직(OnCreate, OnCreateView)에서 ActivityResultCallback 구현을 registerForActivityResult() 호출에 전달합니다. ActivityResultCallback은 앱이 권한 요청에 대한 사용자의 응답을 처리하는 방법을 정의합니다.

 

- ActivityResultLauncher 유형인 registerForActivityResult()의 반환 값을 계속 참조합니다.

 

필요할 때 시스템 권한 대화상자를 표시하려면 이전 단계에서 저장한 ActivityResultLauncher 인스턴스에서 launch() 메서드를 호출합니다.

 

- launch()가 호출되면 시스템 권한 대화상자가 표시됩니다.

사용자가 선택하면 시스템은 개발자가 이전 단계에서 정의한 ActivityResultCallback 구현을 비동기적으로 호출합니다.

 

*  launch()를 호출할 때 표시되는 대화상자를 맞춤설정할 수 없습니다.

사용자에게 더 많은 정보나 컨텍스트를 제공하려면 앱의 UI를 변경하여 사용자가 앱의 기능에 특정 권한이 필요한 이유를 더 쉽게 알 수 있도록 합니다. 예를 들어 기능을 사용 설정하는 버튼의 텍스트를 변경할 수 있습니다.

 

시스템 권한 대화상자의 텍스트는 개발자가 요청한 권한과 연결된 권한 그룹을 참조합니다.

이 권한 그룹은 시스템의 사용 편의성을 위해 설계되었으며 앱은 특정 권한 그룹 내부 또는 외부에 있는 권한에 의존해서는 안 됩니다

 

ActivityResultCallback(허용 및 거부에 했을 때 상황에 맞추어 불려지는 콜백이다.)

registerForActivityResult() 위의 콜백을 등록하고 ActivityResultLauncher을 반환한다.

ActivityResultLauncher. launch()를 실행하면 사용자한테 요청한다. 

// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher. You can use either a val, as shown in this snippet,
// or a lateinit var in your onAttach() or onCreate() method.
val requestPermissionLauncher =
    registerForActivityResult(RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // Permission is granted. Continue the action or workflow in your
            // app.
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied. At the
            // same time, respect the user's decision. Don't link to system
            // settings in an effort to convince the user to change their
            // decision.
        }
    }

 

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // 이미 권한부여를 받았기에 권한이 필요한 작업 수행
    }
    shouldShowRequestPermissionRationale(...) -> {
        // In an educational UI,
        // 왜 권한이 필요한지 구체적인 설명 In this UI,
        // 취소 버튼을 만들어서 권한이 없어도 앱을 쓸수있도록 해라
     
        showInContextUI(...) // 교육용 보여주기.
    }
    else -> {
        // 바로 권한요청하는 경우
        // The registered ActivityResultCallback gets the result of this request.
        requestPermissionLauncher.launch(
                Manifest.permission.REQUESTED_PERMISSION)
    }
}

 

직접 개발자가 권한 요청 코드를 관리하도록 하기

위에서는 콜백만 정의후 .launch 를 통해 OS가 해준것을 대신 하는것. 

requestPermissions() / requestMultiplePermissions() 직접 권한 요청을 한다.

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
       // 권한이 이미 허용되어서 권한으로 작업을 진행한다. 
    }
    shouldShowRequestPermissionRationale(...) -> {
		// 교육용 UI를 보여준다. 
        showInContextUI(...)
    }
    else -> {
       // 직접 권한 요청을 시도한다. 
        requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                REQUEST_CODE)
    }
}

요청코드에 맞추어 권한/ 허용여부 배열을 받아 처리한다. 

override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        PERMISSION_REQUEST_CODE -> {
            // 권한이 거부 되면 해당 권한의 array 참조는 null 이다.
            if ((grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
            	// 사용자가 권한을 허용해줌. 
            } else {
              // 사용자가 권한을 거부하였음.
              // 거부로 인해 사용하지 못하는 기능을 알려줌
              // 사용자의 거부의사를 존중해서 강제로 권한설정화면으로 이동시키지 말것. 
            }
            return
        }

        // 또다른 요청코드가 있다면 when으로 처리한다. 
      
        else -> {
            // Ignore all other requests.
        }
    }
}

 

권한 거부 처리

사용자가 권한 요청을 거부하면 앱에서는 사용자가 권한 거부에 따른 영향을 이해하도록 지원해야 합니다.

특히 앱은 권한이 없어서 작동하지 않는 기능을 사용자가 인식하도록 해야 합니다.

 

  • 사용자의 주의를 유도합니다. 앱에 필요한 권한이 없어서 기능이 제한된 앱 UI의 특정 부분을 강조표시합니다. 다음과 같은 작업을 실행할 수 있습니다.
    • 기능의 결과나 데이터가 나타난 메시지를 표시합니다.
    • 오류 아이콘과 색상이 포함된 다른 버튼을 표시합니다.
  • 자세히 설명합니다. 일반적인 메시지를 표시하지 않습니다. 대신 앱에 필요한 권한이 없어서 어떤 기능을 사용할 수 없는지 언급합니다.
  • 사용자 인터페이스를 차단하지 않습니다. 즉, 사용자가 앱을 계속 사용하는 것을 막는 전체 화면 경고 메시지를 표시하지 않습니다.

동시에 앱은 권한을 거부하겠다는 사용자의 결정을 존중해야 합니다.

 

Android 11(API 수준 30)부터 사용자가 앱이 기기에 설치된 전체 기간 동안 특정 권한에 관해 거부를 두 번 이상 탭하면 앱에서 그 권한을 다시 요청하는 경우 사용자에게 시스템 권한 대화상자가 표시되지 않습니다.

 

이러한 사용자의 작업은 '다시 묻지 않음'을 의미합니다. 이전 버전에서는 사용자가 이전에 '다시 묻지 않음' 체크박스 또는 옵션을 선택하지 않는 한 앱에서 권한을 요청할 때마다 사용자에게 시스템 권한 대화상자가 표시되었습니다.

 

어떤 상황에서는 사용자의 조치가 없어도 권한이 자동으로 거부될 수 있습니다.(android 11부터는 오래동안 사용하지 않는 앱에서는 런타임 권한을 OS가 알아서 허용된 권한을 해제한다.)

마찬가지로 권한이 자동으로 부여될 수도 있습니다. 자동 동작에 관해 어떤 가정을 하고 코드를 작성해선 안된다.

 

앱에서 권한이 필요한 기능에 액세스해야 할 때마다 앱에 여전히 권한이 부여되어 있는지 확인해야 합니다.

 

일회성 권한

Android 11(API 수준 30)부터 앱이 위치, 마이크 또는 카메라와 관련된 권한을 요청할 때마다 사용자에게 표시되는 권한 대화상자에 이번만 허용이라는 옵션이 포함됩니다. 사용자가 대화상자에서 이 옵션을 선택하면 임시 일회성 권한이 앱에 부여됩니다.

 

그러면 앱의 동작과 사용자의 작업에 따라 일정 시간 동안 관련 데이터에 액세스할 수 있습니다.

  • 앱의 activity가 표시되는 동안 앱에서 데이터에 액세스할 수 있습니다.
  • 사용자가 앱을 백그라운드로 보내면 앱에서 짧은 시간 동안 데이터에 계속 액세스할 수 있습니다.
  • activity가 표시되는 동안 포그라운드 서비스가 실행되고 사용자가 앱을 백그라운드로 이동하면 포그라운드 서비스가 중지될 때까지 앱에서 데이터에 계속 액세스할 수 있습니다.
  • 사용자가 일회성 권한을 취소하면(예: 시스템 설정에서) 포그라운드 서비스 실행 여부와 상관없이 앱에서 데이터에 액세스할 수 없습니다. 다른 권한과 마찬가지로 사용자가 앱의 일회성 권한을 취소하면 앱의 프로세스가 종료됩니다.

사용하지 않는 앱의 권한 자동 재설정

앱이 Android 11(API 수준 30) 이상을 타겟팅하고 몇 달 동안 사용되지 않은 경우 시스템에서는 사용자가 앱에 부여한 민감한 런타임 권한을 자동으로 재설정하여 사용자 데이터를 보호합니다. 이 작업은 사용자가 시스템 설정에서 권한을 확인하고 앱의 액세스 수준을 거부로 변경한 것과 같은 효과를 발휘합니다.

 

사용자에게 자동 재설정 사용 중지 요청

 

필요한 경우 사용자에게 시스템이 앱 권한을 재설정하는 것을 방지하라고 요청할 수 있습니다. 이는 다음 사용 사례와 같이 사용자가 앱과 상호작용하지 않아도 백그라운드에서 앱이 주로 작동한다고 사용자가 예상하는 상황에서 유용합니다.

 

사용자가 특정 앱의 권한 자동 재설정을 사용 중지함

  • 가족의 안전 제공
  • 데이터 동기화
  • 스마트 기기와 통신
  • 호환 기기와 페어링

사용자를 시스템 설정의 앱 페이지로 안내하려면 Intent.ACTION_AUTO_REVOKE_PERMISSIONS 인텐트 작업이 포함된 인텐트를 호출합니다. 이 화면에서 사용자는 다음을 실행하여 시스템이 앱 권한을 재설정하는 것을 방지할 수 있습니다.

  1. 권한을 탭하면 앱 권한 설정 화면이 로드됩니다.
  2. 그림 3과 같이 앱이 사용되지 않는 경우 권한 삭제 옵션을 사용 중지합니다.

자동 재설정 사용 중지 여부 확인

 

앱에 자동 재설정 기능이 사용 중지되어 있는지 확인하려면 isAutoRevokeWhitelisted()를 호출합니다. 이 메서드가 true를 반환하면 시스템에서 앱 권한을 자동 재설정하지 않습니다.

 

필요 시 기본 핸들러로 요청

일부 앱은 통화 기록 및 SMS 메시지와 관련된 민감한 사용자 정보에 액세스해야 합니다. 통화 기록 및 SMS 메시지에 특정한 권한을 요청하고 Play 스토어에 앱을 게시하려면 런타임 권한을 요청하기 전에 사용자에게 허용 여부 메시지를 표시하여 앱을 핵심 시스템 기능의 기본 핸들러로 설정하도록 해야 합니다. 기본 핸들러 및 기본 핸들러 허용 여부 메시지를 사용자에게 표시하는 방법에 관한 자세한 내용은 기본 핸들러에서만 사용되는 권한에 관한 가이드를 참조하세요.

'Android Basic' 카테고리의 다른 글

Android Context란?  (0) 2024.09.22
ViewStub  (0) 2021.04.27
permission 개요 및 권한 선언 평가하기  (0) 2021.03.18
viewpager2  (0) 2021.02.22
ViewPager  (0) 2021.02.22