2017년 1월 12일 목요일

Android 6.0 버전 대상 App에서의 permission 요청

기존에 만들었던 앱을 변경해서 올리던 중
SMS receving이 안되어 원인을 찾다보니
아래 내용을 깜박 잊고 있었음.. ㅎㅎ

하긴 2015년 7월에 들은 내용이니....
개인 앱 개발이라 뭐.. 게을러도 너무 게으르다는 생각을 ㅎㅎ

http://charlie0301.blogspot.kr/2015/07/google-io-extended-2015-android-track.html

[런타임 앱 권한]

- 기존에 설치 시 권한을 요청하고 일괄 승인 했던 것을
M target app에서는 이전처럼 manifest에 권한을 명시하더라도
권한을 사용할 때 사용자에게 다시 물어보도록 함.
또한 환경설정에서 앱별 권한을 설정할 수 도 있음.

- Runtime 시 개별적으로 권한을 요청 할 때 시스템 프로세스 상에서 권한 요청이 실행 됨.
- 액티비티 뿐만 아니라 서비스에서 Runtime시 permission 요청을 해야 할 수 있음.

- 콜백으로 사용자 응답 받게 되므로 기존 권한을 사용하던 부분을 callback trigger를 전후로 나눠 처리 해야 함.

- M 이전 Android 버전들 타겟 앱은 이전 방식 처럼 권한을 수락하게 됨.

- M 버전 기반 앱의 권한이 거부되면 안드로이드에서는 가짜 데이터를 제공할 예정이고 앱의 동작은 가늠할 수 없으므로 앱이 권한 거부 상황을 잘 처리하도록 만들어야 함.

- 권한이 변경되더라도 자동으로 업데이트 가능함. (설치 시 권한 수락이 필요 없으니)



Android 6.0 버전 이상을 대상으로 하는 App의 Manifest에 명시된 앱 권한이 사용자로 부터 허락되지 않을 경우 아래와 같은 에러 메세지가 log로 표시된다.

Permission Denial: receiving Intent { act=android.provider.Telephony.SMS_RECEIVED flg=0x18000010 (has extras) } to com.blogspot.charlie0301/kr.blogspot.charlie0301.wimple.SMSReceiver requires android.permission.RECEIVE_SMS due to sender com.android.phone (uid 1001)

동일 case : http://stackoverflow.com/questions/32537156/basic-error-in-androidmanifest-xml-for-sms-receiving-permission


그래서 App에서 해당 권한이 필요한 기능을 사용할 때 사용자에게 팝업을 띄워 권한을 요청해야 한다. 자세한 설명은 아래에 잘 번역 되어 있음.
: https://developer.android.com/training/permissions/declaring.html

그래서 대충 copy & paste로 다음과 같이 붙임.

권한 요청 시점
 : 설정에서 SMS read 하는 기능을 on/off 하는 부분이 있어 on 될 때 권한을 요청하도록 함.
 : https://github.com/hallower/wimple/blob/master/app/src/main/java/kr/blogspot/charlie0301/wimple/SettingsFragment.java#L121

권한 요청 팝업 표시
 : ContextCompat.checkSelfPermission()로 권한이 수락되지 않았는지 확인하여 ActivityCompat.shouldShowRequestPermissionRationale()로 권한 수락에 대한 설명이 필요한 지 확인함.

[구글 가이드내 코드] 
// 권한이 필요한 시점에
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // 사용자가 이미 Deny를 했는지 확인하는 절차
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Deny를 했다면 적절한 설명을 보여주고 사용자의 반응에 따라 다시 요청 해야 함.

    } else {

        // 권한 요청을 하지 않았었으면 권한을 요청함.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS 은 
        // callback에서 권한 처리 결과를 받기 위한 상수값 
    }
}

실제 구현은 귀찮아서 그냥 deny하면 설명만 보여주게 하였음.

 : https://github.com/hallower/wimple/blob/master/app/src/main/java/kr/blogspot/charlie0301/wimple/WimpleActivity.java#L168



사용자의 권한 수락/거절 이후 처리
 : 이후 사용자의 수락/거절에 따라 callback method가 호출되어 결과를 알 수 있음.


@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // 요청이 취소되었을 경우 result가 null일 수 있어 에러 처리 필요
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // 권한이 수락되었음

            } else {

                // 권한이 거절됨...
            }
            return;
        }

        // 다른 권한에 대해서 case 추가 처리 하면 됨.
    }
}

일단 구현에서는 그냥 메세지만 보여주는 것으로 간단하게 대충 구현함.

 : https://github.com/hallower/wimple/blob/master/app/src/main/java/kr/blogspot/charlie0301/wimple/WimpleActivity.java#L181


안드로이드 사이트에서 설명하고 있지만 특히나 고려해야할 사항이

- 권한 요청 시점을 필요한 시점으로 선택해야 한다.
 : 일부 앱에서는 시작 시 무조건 권한을 확인하게 하던데 별로 좋지 않은 방법인 듯 하다.
 : 특히나 위 예제를 시작 시점에 붙여 넣게 되면 Deny 되었을 경우 앱 실행 시 마다 권한 설명을 표시 되거나 권한을 재요청을 하게 하는 loop이 생길 수 있음.
 : 권한을 사용하는 시점에 요청 하는 것이 가장 바람직한 듯 함.


- 사용자가 거절 했을 경우 이후 처리를 잘해야 겠음.
 : 제한된 메세지 내용을 통해 안드로이드 설정의 앱 목록에서 선택하여 권한을 설정하라는 가이드하는 것은 어려울 것 같음.
 : 그래서 간단한 권한 설명과 사용자가 메세지를 읽고 적절한 시점에 권한을 요청해야 할 것 같음.
 : 그냥 무조건 선택하라는 안내는 더 도움이 안될 것 같고

댓글 없음:

댓글 쓰기