마시멜로(API23)부터 권한이 2가지로 분류됨.

  • 일반 권한 : 앱을 설치할 때 부여됨.
  • 위험 권한 : 앱 실행 시 사용자에게 권한을 부여할 지 물어보고 부여됨. (ex. 위치, 카메라, 마이크, 연락처, 전화, 문자, 일정, 센서 등)
더보기

위험권한의 종류

  • LOCATION(위치) : ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
  • CAMERA : CAMERA
  • MICROPHONE : RECORD_AUDIO
  • CONTACTS : READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS
  • PHONE : READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG, WRITE_CALL_LOG, USE_SIP, ADD_VOICEMAIL, PROCESS_OUTGOING_CALLS
  • SMS : SEND_SMS, RECEIVE_SMS, READ_SMS, RECEIVE_WAP_PUSH, RECEIVE_MMS
  • CALENDAR : READ_CALENDAR, WRITE_CALENDAR
  • SENSORS : BODY_SENSORS
  • STORAGE(SD카드 접근) : READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE

위험권한을 부여하는 방법은 2가지가 있다.

  • 직접 부여하기
  • 외부 라이브러리를 이용하여 부여하기 (더 간단)

<직접 부여하기>

1. AndroidManifest.xml에 다음 코드를 추가한다.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission> 태그를 사용하여 읽기, 쓰기 권한을 추가한다.

 

2. MainActivity.java

전체 코드

더보기
package com.example.chapter6_3;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] permissions = {
                android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        checkPermissions(permissions);
    }

    public void checkPermissions(String[] permissions){
        ArrayList<String> targetList = new ArrayList<String>();

        int len = permissions.length;
        for(int i=0;i<len;i++){
            String curPermission = permissions[i];
            int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);

            if(permissionCheck== PackageManager.PERMISSION_GRANTED)
                Toast.makeText(this, curPermission+" 권한 있음.", Toast.LENGTH_LONG).show();
            else{
                Toast.makeText(this,curPermission+"권한 없음.",Toast.LENGTH_LONG).show();
                if(ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission))
                    Toast.makeText(this, curPermission+" 권한 설명 필요함", Toast.LENGTH_LONG).show();
                else
                    targetList.add(curPermission);
            }
        }
        String[] targets = new String[targetList.size()];

        targetList.toArray(targets);
        if(targets.length>0)
            ActivityCompat.requestPermissions(this, targets, 101); // 위험권한 부여 요청하기
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch(requestCode){ // 요청 코드가 맞는지 확인한다.
            case 101:{
                if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED)
                    Toast.makeText(this, "첫번째 권한을 사용자가 승인한.", Toast.LENGTH_LONG).show();
                else
                    Toast.makeText(this, "첫번째 권한 거부됨.", Toast.LENGTH_LONG).show();
            }
            return;
        }
    }
}
  • onCreate
더보기
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] permissions = {
                android.Manifest.permission.READ_EXTERNAL_STORAGE,
                android.Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        checkPermissions(permissions);
    }

 checkPermissions 메서드를 호출한다.

 

[ 에러 발생 ]

책에 있는대로 작성하면 에러가 발생하니까 앞에 android를 붙여주었다.

 

 

Runtime permission check API 23+: Cannot resolve symbol 'Manifest.permission.READ_EXTERNAL_STORAGE'

I'm trying to check a permission in API 23 and above like this: int result = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); But it can't find Manifest.permiss...

stackoverflow.com

 

  • checkPermissions
더보기
  public void checkPermissions(String[] permissions){
      ArrayList<String> targetList = new ArrayList<String>();

      int len = permissions.length;
      for(int i=0;i<len;i++){
          String curPermission = permissions[i];
          int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);

          if(permissionCheck== PackageManager.PERMISSION_GRANTED)
              Toast.makeText(this, curPermission+" 권한 있음.", Toast.LENGTH_LONG).show();
          else{ // 권한이 부여되지 않은 상황
              Toast.makeText(this,curPermission+"권한 없음.",Toast.LENGTH_LONG).show();
              if(ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission))
                  Toast.makeText(this, curPermission+" 권한 설명 필요함", Toast.LENGTH_LONG).show();
              else
                  targetList.add(curPermission);
          }
      }
        String[] targets = new String[targetList.size()];

        targetList.toArray(targets);
        if(targets.length>0)
            ActivityCompat.requestPermissions(this, targets, 101); // 위험권한 부여 요청하기
    }

 

for문 (i는 0부터 permissions.length 전까지. 예제의 경우 읽기랑 쓰기 권한을 요청하므로 length는 2이다.)

if GRANTED, 권한이 있을 경우 → '권한 있음' 토스트 메시지 출력 (권한 허용(Allow) 버튼을 누를 경우)

else, 권한이 없는 경우 → '권한 없음' 토스트 메시지 출력 (권한 허용(Allow) 버튼을 누르지 않은 경우)

 

여기서 권한이 없는 경우에서 if(권한설명필요함)과 else로 나뉘어지는데,

처음에 권한 허용을 하지 않은 상태에서는 아무일도 일어나지 않지만 앱을 나갔다가 다시 들어가면

shouldShowRequestPermissionRationale()가 1을 넘겨준다. (권한 허용 안함(Don't allow) 버튼을 누를 경우)

 

(shouldShowRequestPermissionRationale() : 사용자가 이전에 권한 요청을 거부한 경우 true 값을 넘겨준다.

그 결과를 이용하여 앱을 사용하려면 권한이 필요함을 사용자에게 알려 주는 안내를 추가해야 합니다.) 

 

맨 처음 앱에 들어갔을 때는 당연히 Allow나 Don't Allow 둘 다 누르지 않은 상태이기 때문에

"권한 없음" 메시지가 먼저 뜨고, targetlist에 curPermission(읽기권한,쓰기권한)이 추가된다. 

 

 

안드로이드 권한 요청 예제

안드로이드 권한 요청 및 획득

velog.io

 

[ 에러 발생 ]

마지막 부분의 원래 코드는 아래와 같았다. if 조건이 없었음.

ActivityCompat.requestPermissions(this, targets, 101); // 위험권한 부여 요청하기

 근데 permission cannot be null or empty 에러가 떠서 알아보니 이전에 권한 수락을 해서 더 이상 권한이 없는데 다시 요청하려고 해서 발생한 문제라고 한다. targets.length>0 일 때만 수행되도록 수정.

 

[Android] 위험 권한(Permission) 부여하기

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 위험 권한 대부분 주요 권한들은 개인 정보가 담겨 있는 정보에 접근하거나 개인정보를 만들어 낼 수 있는 단말의 주

junyoung-developer.tistory.com

  • onRequestPermissioinsResult
더보기
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch(requestCode){ // 요청 코드가 맞는지 확인한다.
            case 101:{
                if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED)
                    Toast.makeText(this, "첫번째 권한을 사용자가 승인한.", Toast.LENGTH_LONG).show();
                else
                    Toast.makeText(this, "첫번째 권한 거부됨.", Toast.LENGTH_LONG).show();
            }
            return;
        }
    }

아래는 checkPermissions 메서드에서 requestPermissions 메서드를 호출하는 코드이다.

권한을 부여하기 위해 requestCode로 101을 보낸 것을 확인할 수 있다.

ActivityCompat.requestPermissions(this, targets, 101); // 위험권한 부여 요청하기

 

requestCode가 101이면서 grantResults의 길이가 0보다 크고, grantResults[0]에 PackageManater.PERMISSON_GRANTED가 들어가면 승인.

여기선 2개의 권한을 수락해달라고 했으니 grantResult의 길이는 2이다. 그리고 권한 수락 시 알아서 grantResult[0]에는 PackageManater.PERMISSON_GRANTED 상수가 들어간다고 함.

3. 실행 결과

  • 승인할 경우

캡처 하나 빼먹었는데 WRITE_... 권한 없음도 토스트메시지로 나옴.

  • 승인하지 않을 경우

 

<외부 라이브러리를 이용하여 부여하기>

 

1. 매니패스트 파일 작성

더보기

위에서 작성한 코드와 동일하다.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2. build.gradle(app)에 코드 추가

더보기

아 참고로 소스코드는 자바를 쓰는데 그래들은 코틀린을 쓴다. 어쩌다 보니 이렇게 되었다.

dependencies {
    implementation("com.yanzhenjie:permission:2.0.0")

책에서는 2.0.3을 쓰라고 했는데, 라이브러리가 자동 import 되지 않는 것이 확인 되었다. (AndPermission 빨간색)

참고한 사이트
 

(해결함) 두번째마당 06 / SampleReiver / p339 질문있습니다. com.yanzhenjie:permission:2.0.3 문제

대한민국 모임의 시작, 네이버 카페

cafe.naver.com

어떤 분은 2.0.2를 사용했더니 해결되었다고도 한다.

나는 이럴 경우 라이브러리의 최신 버전을 사용해야 해결되는 줄 알았는데 버전이 맞는 걸 그냥 이것저것 숫자 바꿔가면서 시도를 해야하는 듯..

3. MainActivity.java

더보기
package com.example.chapter6_3;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AndPermission.with(this).runtime().permission(
                Permission.READ_EXTERNAL_STORAGE,
                Permission.WRITE_EXTERNAL_STORAGE).onGranted(new Action<List<String>>(){
            @Override
            public void onAction(List<String> permissions) {
                String message = "허용된 권한 개수"+permissions.size();
                Toast.makeText(getApplicationContext(),message,Toast.LENGTH_LONG).show();
            }
        }).onDenied(new Action<List<String>>() {
            @Override
            public void onAction(List<String> permissions) {
                String message = "거부된 권한 개수"+permissions.size();
                Toast.makeText(getApplicationContext(),message,Toast.LENGTH_LONG).show();
            }
        }).start();
    }
}

 코드.. 뭔가 좀 어려움. 일단 그냥 넘어가자.

대충 직접 권한을 부여하는 것보다 이렇게 권한을 부여하는게 더 간단하다는 것만 알자.

4. 실행 결과

 

권한을 거부했을 때 : "거부된 권한 개수2"로 뜸

권한을 허용했을 때 : "거부된 권한 개수1"로 뜸

 

왜.. 허용된 권한 개수2로 안뜨는거지? 아아... 모르겠다.

 

암튼 라이브러리에 대해 좀 더 알아가는 시간을 가졌다.