반응형

안드로이드 6.0 마시멜로 ( API 23 ) 이상부터 안드로이드 디바이스의 자원을 사용하려면 런타임 퍼미션( runtime permissions)이 필요합니다.


본 포스팅에서는 런타임 퍼미션을 체크하고 요청하는 방법을 설명하고 있습니다.


  • 퍼미션 체크 ActivityCompat.checkSelfPermission(Context, String)

  • 퍼미션 요청 ActivityCompat.requestPermissions(Activity, String[], int)

  • 퍼미션 요청 콜백함수 ActivityCompat.OnRequestPermissionsResultCallback



카메라, 외부저장소 등에 접근하기 위한 퍼미션 요청을 사용자가 허용해야만 앱에서 해당 하드웨어를 사용할 수 있습니다.

.


최초 작성 - 2018. 8. 15




포스팅에서는 다음 포스팅의 코드에서 런타임 퍼미션 관련 부분만 설명하고 있습니다.



안드로이드  카메라 예제 ( 프리뷰 및 사진찍기 )

http://webnautes.tistory.com/822





MainActivity 액티비티는 앱에서 가장 먼저 실행되는 액티비티입니다. 런처 액티비티(Launcher Activity)라고도 부릅니다.

보통 이 액티비티에서 퍼미션 요청을 한꺼번에 하지만 카메라, 외부 저장소등을 접근하기 직전에 해당 퍼미션만 요청을 하기도 합니다.  

예제에서는 MainActivity가 실행되자 마자 카메라 디바이스에 접근하기 위한 퍼미션(Manifest.permission.CAMERA)과 캡처한 영상을 외부 저장소에 저장하기 위한 퍼미션(Manifest.permission.WRITE_EXTERNAL_STORAGE)을 요청합니다.


퍼미션이 허용되고나면 카메라에서 영상을 가져와  보여줄 수 있고, 캡처한 영상을 외부 저장소에 저장할 수 있습니다.



1.  ActivityCompat.checkSelfPermission으로 카메라 및 외부 저장소 퍼미션 상태를 체크합니다.  

   int ActivityCompat.checkSelfPermission(Context context, String permission)


  퍼미션이 허용되어 있는 상태라면 PackageManager.PERMISSION_GRANTED를 리턴하고 거부 되어 있는 상태라면  PackageManager.PERMISSION_DENIED를 리턴합니다.

support library에서 알아서 해주므로 안드로이드 운영체제의 API 레벨을 따로 체크할 필요가 없습니다.




2. 퍼미션이 허용된 상태(PackageManager.PERMISSION_GRANTED)라면 카메라 프리뷰를 시작합니다.


           // 1. 카메라 퍼미션과 외부 저장소 퍼미션을 가지고 있는지 체크합니다.
           int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
           int writeExternalStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);


           if ( cameraPermission == PackageManager.PERMISSION_GRANTED
                   && writeExternalStoragePermission == PackageManager.PERMISSION_GRANTED) {  
               // 2. 이미 퍼미션을 가지고 있다면
               // ( 안드로이드 6.0 이하 버전은 런타임 퍼미션이 필요없기 때문에 이미 허용된 걸로 인식합니다.)

               startCamera();  // 3. 카메라 프리뷰 시작


           }else {  

               . . . . . . . . . . . . . . . . . . . . . . . . . .


           }




3. 퍼미션이 거부된 상태( PackageManager.PERMISSION_DENIED)라면 ActivityCompat.requestPermissions으로 사용자에게 퍼미션 요청을 해야 합니다.

사용자에게 퍼미션 허용을 물어보는 대화상자가 보이게 됩니다.


  @Override
   public void onCreate(Bundle savedInstanceState) {


   . . . . . . . . . . . . . . . . . . . . . . . . . .


       if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {

           // 1. 카메라 퍼미션과 외부 저장소 퍼미션을 가지고 있는지 체크합니다.
           int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
           int writeExternalStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);


           if ( cameraPermission == PackageManager.PERMISSION_GRANTED
                   && writeExternalStoragePermission == PackageManager.PERMISSION_GRANTED) {  

               // 2. 이미 퍼미션을 가지고 있다면
               // ( 안드로이드 6.0 이하 버전은 런타임 퍼미션이 필요없기 때문에 이미 허용된 걸로 인식합니다.)


               startCamera(); // 3. 카메라 프리뷰 시작


           }else {  //2. 퍼미션 요청을 허용한 적이 없다면 퍼미션 요청이 필요합니다. 2가지 경우(3-1, 4-1)가 있습니다.

               // 3-1. 사용자가 퍼미션 거부를 한 적이 있는 경우에는
               if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])
                       || ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[1])) {

                   // 3-2. 요청을 진행하기 전에 사용자가에게 퍼미션이 필요한 이유를 설명해줄 필요가 있습니다.
                   Snackbar.make(mLayout, "이 앱을 실행하려면 카메라와 외부 저장소 접근 권한이 필요합니다.",
                           Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                       @Override
                       public void onClick(View view) {

                           // 3-3. 사용자게에 퍼미션 요청을 합니다. 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                           ActivityCompat.requestPermissions( MainActivity.this, REQUIRED_PERMISSIONS,
                                   PERMISSIONS_REQUEST_CODE);
                       }
                   }).show();


               } else {
                   // 4-1. 사용자가 퍼미션 거부를 한 적이 없는 경우에는 퍼미션 요청을 바로 합니다.
                   // 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                   ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS,
                           PERMISSIONS_REQUEST_CODE);
               }

           }

       } else {

           final Snackbar snackbar = Snackbar.make(mLayout, "디바이스가 카메라를 지원하지 않습니다.",
                   Snackbar.LENGTH_INDEFINITE);
           snackbar.setAction("확인", new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   snackbar.dismiss();
               }
           });
           snackbar.show();
       }





4. 요청 결과는 android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback에 리턴됩니다.

    void onRequestPermissionsResult (int requestCode, String[] permissions,  int[] grantResults)


// 퍼미션 요청(ActivityCompat.requestPermissions)에 대한 결과를 리턴받으려면

// OnRequestPermissionsResultCallback 콜백의 onRequestPermissionsResult 메소드를 구현해줘야 합니다.

public class MainActivity extends AppCompatActivity
       implements ActivityCompat.OnRequestPermissionsResultCallback {    


   . . . . . . . . . . . . . . . . . . . . . . . . . .


   // onRequestPermissionsResult에서 수신된 결과에서 ActivityCompat.requestPermissions를 사용한 퍼미션 요청을 구별하기 위해 사용됩니다.  

   private static final int PERMISSIONS_REQUEST_CODE = 100;         


   // 앱을 실행하기 위해 필요한 퍼미션을 정의합니다.  

   String[] REQUIRED_PERMISSIONS  = {Manifest.permission.CAMERA, // 카메라
                                     Manifest.permission.WRITE_EXTERNAL_STORAGE};  // 외부 저장소


   . . . . . . . . . . . . . . . . . . . . . . . . . .

   

    /*
    * ActivityCompat.requestPermissions를 사용한 퍼미션 요청의 결과를 리턴받는 메소드입니다.
    */

   @Override

   public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grandResults) {

               




5. onRequestPermissionsResult에서 퍼미션을 물어보는 대화상자를 보고 사용자가 어떤 선택을 했는지 체크합니다.

결과가 퍼미션 허용이라면 CameraPreviewActivity가 시작되며 거부했다면 사용자에게 퍼미션이 필요한 이유를 설명한 후  앱을 종료합니다.



   @Override
   public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grandResults) {

       if ( requestCode == PERMISSIONS_REQUEST_CODE && grandResults.length == REQUIRED_PERMISSIONS.length) {

           // 요청 코드가 PERMISSIONS_REQUEST_CODE 이고, 요청한 퍼미션 개수만큼 수신되었다면

           boolean check_result = true;

           // 모든 퍼미션을 허용했는지 체크합니다.

           for (int result : grandResults) {
               if (result != PackageManager.PERMISSION_GRANTED) {
                   check_result = false;
                   break;
               }
           }


           if ( check_result ) {

               // 모든 퍼미션을 허용했다면 카메라 프리뷰를 시작합니다.  

               startCamera();
           }
           else {
               // 거부한 퍼미션이 있다면 앱을 사용할 수 없는 이유를 설명해주고 앱을 종료합니다.2 가지 경우가 있습니다.

               if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])
                       || ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[1])) {

                   // 사용자가 거부만 선택한 경우에는 앱을 다시 실행하여 허용을 선택하면 앱을 사용할 수 있습니다.

                   Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 앱을 다시 실행하여 퍼미션을 허용해주세요. ",
                           Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                       @Override
                       public void onClick(View view) {

                           finish();
                       }
                   }).show();

               }else {

                      
                   // “다시 묻지 않음”을 사용자가 체크하고 거부를 선택한 경우에는 설정(앱 정보)에서 퍼미션을 허용해야 앱을 사용할 수 있습니다.
                   Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 설정(앱 정보)에서 퍼미션을 허용해야 합니다. ",
                           Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                       @Override
                       public void onClick(View view) {

                           finish();
                       }
                   }).show();
               }
           }

       }


   }

            

                                     

참고

Android RuntimePermissionsBasic Sample

https://github.com/googlesamples/android-RuntimePermissionsBasic













반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts