마시멜로 버전부터는 권한을 일반 권한과 위험 권한으로 나누었다.
앱을 설치하는 시점에서 사용자에게 물어보는 기존 방식은 사용자가 아무런 생각 없이 앱을 설치하는 경우가 많았으며 이에 따라 설치된 앱들이 단말의 주요 기능을 마음대로 사용할 수 있는 위험이 있었다.
이 때문에 위험 권한으로 분류된 권한들에 대해서는 앱을 설치할 때가 아니라 앱을 실행할 때 권한을 부여하도록 변경되었다.
위험 권한으로 분류된 주요 권한들은 대부분 개인정보가 담겨있는 정보에 접근하거나 개인정보를 만들어낼 수 있는 단말의 주요 장치에 접근할 때 부여된다.
위험 권한 그룹의 분류(Permission Group) | 세부 권한(Permission) |
---|---|
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 ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
SMS | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
CALENDAR | READ_CALENDAR WRITE_MMS |
SENSORS | BODY_SENSORS |
STORAGE | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
SD 카드에 접근할 때 사용되는 두 가지 위험 권한을 부여하기 위해 먼저 AndroidManifest.xml파일에 다음과 같이 권한을 추가한다.
기본 권한을 부여할 때는 <uses-permission>
태그를 사용한다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.techtown.permission">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
부여한 기본 권한 중에서 SD 카드에 접근하는 권한은 위험 권한이므로 코드를 추가로 입력해야 한다.
MainActivity.java 파일에 다음과 같이 코드를 추가한다.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
checkPermissions(permissions);
}
public void checkPermissions(String[] permissions) {
ArrayList<String> targetList = new ArrayList<String>();
for(int i = 0; i < permissions.length; i++) {
String curPermission = permissions[i];
int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);
if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, curPermission + " 권한 있음.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, curPermission + " 권한 없음.", Toast.LENGTH_SHORT).show();
if(ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission)) {
Toast.makeText(this, curPermission + " 권한 설명 필요함.", Toast.LENGTH_SHORT).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) {
switch (requestCode) {
case 101 : {
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "첫 번째 권한을 사용자가 승인함.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "첫 번째 권한 거부됨.", Toast.LENGTH_SHORT).show();
}
return;
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
SD 카드에 접근하기 위해서는 READ_EXTERNAL_STORAGE와 WRITE_EXTERNAL_STORAGE 두 개의 권한이 필요하다.
위험 권한을 부여할 권한을 문자열에 넣은 후 직접 정의한 checkPermissions 메소드를 사용해 지정한 위험 권한을 부여할지 판단한다.
String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
checkPermissions(permissions);
checkPermissions 메소드는 지정한 권한들에 대해서 그 권한이 부여되어 있는지 확인하고, 부여되지 않았다면 ArrayList 안에 넣었다가 부여되지 않은 권한들만 권한 요청을 하게 된다.
public void checkPermissions(String[] permissions) {
ArrayList<String> targetList = new ArrayList<String>();
for(int i = 0; i < permissions.length; i++) {
String curPermission = permissions[i];
int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);
if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, curPermission + " 권한 있음.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, curPermission + " 권한 없음.", Toast.LENGTH_SHORT).show();
if(ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission)) {
Toast.makeText(this, curPermission + " 권한 설명 필요함.", Toast.LENGTH_SHORT).show();
} else {
targetList.add(curPermission);
}
}
}
String[] targets = new String[targetList.size()];
targetList.toArray(targets);
if(targets.length > 0)
ActivityCompat.requestPermissions(this, targets, 101);
}
먼저 부여되지 않은 권한들을 저장할 ArrayList를 만든다.
ArrayList<String> targetList = new ArrayList<String>();
지정된 권한들의 개수만큼 for문을 반복한다.
for(int i = 0; i < permissions.length; i++) {
String curPermission = permissions[i];
지정된 권한이 이미 부여되어 있는지 ContextCompat 클래스의 checkSelfPermission 메소드를 통해 확인한다.
int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);
권한이 부여되어 있는지, 아닌지에 따라 다른 Toast 를 출력한다.
if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, curPermission + " 권한 있음.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, curPermission + " 권한 없음.", Toast.LENGTH_SHORT).show();
권한이 부여되있지 않다면 Toast 출력 후,
사용자가 권한 요청을 명시적으로 거부한 경우를 ActivityCompat 클래스의 shouldShowRequestPermissionRationale 메소드로 확인하여
True라면 Toast 를 출력한다.
False라면 부여되지 않은 권한으로 ArrayList에 추가한다.
if(ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission)) {
Toast.makeText(this, curPermission + " 권한 설명 필요함.", Toast.LENGTH_SHORT).show();
} else {
targetList.add(curPermission);
}
부여되지 않은 권한들을 저장한 ArrayList를 String 배열로 바꾼 후,
ActivityCompat 클래스의 requestPermissions 메소드로 권한 부여 요청 대화상자를 띄워준다.
String[] targets = new String[targetList.size()];
targetList.toArray(targets);
if(targets.length > 0)
ActivityCompat.requestPermissions(this, targets, 101);
onRequestPermissionsResult 메소드를 재정의하였다.
onRequestPermissionsResult 메소드에는 요청 코드와 함께 사용자가 권한을 수락하였는지 여부가 파라미터로 전달된다.
사용자가 권한을 수락하였다면 PackageManager.PERMISSION_GRANTED 상수가 결과 같으로 확인된다.
첫 번째 권한이 수락되었는지 여부를 확인한 후 토스트 메시지를 띄우도록 하였다.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 101 : {
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "첫 번째 권한을 사용자가 승인함.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "첫 번째 권한 거부됨.", Toast.LENGTH_SHORT).show();
}
return;
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}