[Android] 카메라/이미지 권한 부여 및 사용 하기

원준·2023년 7월 27일

Android Studio

목록 보기
34/40

이게 뭔데?

  • 기본적으로 안드로이드에서 카메라/이미지 폴더에 접근하기 위해서는 권한이 필요하다.
  • 권한 없이 접근할 수 있다면, 누구나 핸드폰의 데이터를 가져오기 가능하기에 파일에 접근하는 것은 권한을 부여해서 가져오도록 되어 있다.
  • 카메라/이미지 같은 경우 권한을 더 줘야할 부분이 있기에 자세히 알아보자.

설정

  1. Modeul 수준의 builde.gradle 설치할 라이브러리 추가
dependencies {
	//...
    implementation 'commons-io:commons-io:2.4'
}
  1. res/xml에 fileprovider.xml 파일을 만들어서 내용을 추가하자.
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <root-path
        name="root"
        path="." />

    <cache-path
        name="cache"
        path="." /> <!--Context.getCacheDir() 내부 저장소-->
    <files-path
        name="files"
        path="." /> <!--Context.getFilesDir() 내부 저장소-->

    <external-path
        name="external"
        path="."/>  <!--  Environment.getExternalStorageDirectory() 외부 저장소-->
    <external-cache-path
        name="external-cache"
        path="."/> <!--  Context.getExternalCacheDir() 외부 저장소-->
    <external-files-path
        name="images"
        path="Pictures" /> <!--  Context.getExternalFilesDir() 외부 저장소-->
</paths>
  1. manifests에 권한에 대해 내용을 추가하자.
	<!--카메라가 없어도 상관없다라는 부분-->
	<uses-feature
        android:name="android.hardware.camera"
        android:required="true" />

	<!-- 이미지 저장 폴더 읽기/쓰기 권한 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<!--카메라 권한 -->
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        <!-- *** -->
                 >
      
		<!--저장소 관련해서 경로 설정-->
    	<provider
            android:authorities="com.프로젝트경로.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/fileprovider" />
        </provider> 
  1. values/strings.xml에 내용을 추가하자.
    • 해당 내용은 여러개의 Text를 사용하기 위해 추가하는 것이다.
<string-array name="alert_photo">
    <item>카메라로 사직찍기</item>
    <item>앨범에서 가져오기</item>
</string-array>

사용하자!

  • 사용하기 전에 여러가지 메서드가 정말 많이 필요하다.
  • 순서대로 추가하자!
  1. 저장할 매게변수 추가

    File photoFile; //사진을 찍었거나, 가져왔거나 저장할 변수
  2. 권한 확인/부여 및 지정된 이미지 저장

      private void showDialog(){
          AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
          builder.setTitle("선택하세요.");
    
          // 다이아로그에 여러개 항목을 보여주게 가능한 함수
          builder.setItems(R.array.alert_photo, new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialogInterface, int i) { // 각각을 클릭했을때 이벤트를 작성
                  // i : 누른게 무엇인지 알려줌
                  if(i == 0){ // 첫번째 항목을 눌렀다면,
                      camera();
                  }else if(i == 1){
                      album();
                  }
              }
          });
    
          builder.show();
      }
        private void camera(){
          // 퍼미션 체크 (권한 여부 체크)
          int permissionCheck = ContextCompat.checkSelfPermission(
                  MainActivity.this, android.Manifest.permission.CAMERA);
    
          // 허용이 안되어있으면,
          if(permissionCheck != PackageManager.PERMISSION_GRANTED){
              // 다시한번 물어 봐라
              ActivityCompat.requestPermissions(MainActivity.this,
                      new String[]{android.Manifest.permission.CAMERA} ,
                      1000);
              Toast.makeText(MainActivity.this, "카메라 권한 필요합니다.",
                      Toast.LENGTH_SHORT).show();
              return;
          }
          // 권한을 이미 허용했다면
          else {
              Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
              //카메라가  있다면 실행
              if(i.resolveActivity(MainActivity.this.getPackageManager())  != null  ){
    
                  // 사진의 파일명을 만들기
                  String fileName = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                  photoFile = getPhotoFile(fileName);
    
                  //android:authorities="com.wonjun.cameraapp.fileprovider"
                  // 동일해야함
                  Uri fileProvider = FileProvider.getUriForFile(MainActivity.this,
                          "com.프로젝트경로.fileprovider", photoFile);
                  i.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider);
                  startActivityForResult(i, 100);
    
              } else{
                  Toast.makeText(MainActivity.this, "이폰에는 카메라 앱이 없습니다.",
                          Toast.LENGTH_SHORT).show();
              }
          }
      }
     private File getPhotoFile(String fileName) {
          File storageDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
          try{
              return File.createTempFile(fileName, ".jpg", storageDirectory);
          }catch (IOException e){
              e.printStackTrace();
              return null;
          }
      }
    // 앨범 선택시
      private void album(){
          if(checkPermission()){ // 권한 여부 확인
              displayFileChoose();
          }else{
              requestPermission();
          }
      }
    // 앨범 권한 부여 체크
    private boolean checkPermission(){
          int result = ContextCompat.checkSelfPermission(MainActivity.this,
                  Manifest.permission.WRITE_EXTERNAL_STORAGE);
          if(result == PackageManager.PERMISSION_DENIED){
              return false;
          }else{
              return true;
          }
      }
    private void requestPermission() {
          if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                  Manifest.permission.WRITE_EXTERNAL_STORAGE)){
              Log.i("DEBUGGING5", "true");
              Toast.makeText(MainActivity.this, "권한 수락이 필요합니다.",
                      Toast.LENGTH_SHORT).show();
          }else{
              Log.i("DEBUGGING6", "false");
              ActivityCompat.requestPermissions(MainActivity.this,
                      new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 500);
          }
      }
    private void displayFileChoose() { //앨범이 열림
          Intent i = new Intent();
          i.setType("image/*");
          i.setAction(Intent.ACTION_GET_CONTENT);
          startActivityForResult(Intent.createChooser(i, "SELECT IMAGE"), 300);
              // 앨범 파일을 선택하면, Main으로 돌아옴 (이전 액티비티)
                  // -> onActivityResult 300번으로 돌아옴
      }
    //앨범에서 선택한 사진이름 가져오기
      public String getFileName( Uri uri ) {
          Cursor cursor = getContentResolver( ).query( uri, null, null, null, null );
          try {
              if ( cursor == null ) return null;
              cursor.moveToFirst( );
              @SuppressLint("Range") String fileName = cursor.getString( cursor.getColumnIndex( OpenableColumns.DISPLAY_NAME ) );
              cursor.close( );
              return fileName;
    
          } catch ( Exception e ) {
              e.printStackTrace( );
              cursor.close( );
              return null;
          }
      }
    // 권한 관련해서 통신할때  사용되는 메서드
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          switch (requestCode) {
              case 1000: { //아까 카메라 권한이  설정안되어 있을때 1000번으로 보냈었음
                  if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                      Toast.makeText(MainActivity.this, "권한 허가 되었음",
                              Toast.LENGTH_SHORT).show();
                  } else {
                      Toast.makeText(MainActivity.this, "아직 승인하지 않았음",
                              Toast.LENGTH_SHORT).show();
                  }
                  break;
              }
              case 500: {
                  if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                      Toast.makeText(MainActivity.this, "권한 허가 되었음",
                              Toast.LENGTH_SHORT).show();
                  } else {
                      Toast.makeText(MainActivity.this, "아직 승인하지 않았음",
                              Toast.LENGTH_SHORT).show();
                  }
    
              }
          }
      }
    // 앱을 실행했을때, 사진에 대해 유저에게 보여주기 위해 실행되는 메서드
      // 카메라로 찍었을때, 사진을 보여준다.
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
          if(requestCode == 100 && resultCode == RESULT_OK){ //카메라
    
              Bitmap photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
    
              ExifInterface exif = null;
              try {
                  exif = new ExifInterface(photoFile.getAbsolutePath());
              } catch (IOException e) {
                  e.printStackTrace();
              }
              int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                      ExifInterface.ORIENTATION_UNDEFINED);
              photo = rotateBitmap(photo, orientation); //가로, 세로 찍은 방향에 대해 정확하게 다시 돌려준다.
    
              // 압축시킨다. 해상도 낮춰서
              OutputStream os;
              try {
                  os = new FileOutputStream(photoFile);
                  photo.compress(Bitmap.CompressFormat.JPEG, 50, os);
                  os.flush();
                  os.close();
              } catch (Exception e) {
                  Log.e(getClass().getSimpleName(), "Error writing bitmap", e);
              }
    
              photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
    
              imageView.setImageBitmap(photo); // 보여줌
              imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    
              // 네트워크로 데이터 보낸다.
                  // 카메라 찍은 동시에 네트워크로 데이터를 보내야 한다면, 코드를 추가한다.
    
          }else if(requestCode == 300 && resultCode == RESULT_OK && data != null &&
                  data.getData() != null){
              // 앨범에서 사진을 가져왔을때
    
              Uri albumUri = data.getData( );
              String fileName = getFileName( albumUri );
              try {
    
                  ParcelFileDescriptor parcelFileDescriptor = getContentResolver( ).openFileDescriptor( albumUri, "r" );
                  if ( parcelFileDescriptor == null ) return;
                  FileInputStream inputStream = new FileInputStream( parcelFileDescriptor.getFileDescriptor( ) );
                  photoFile = new File( this.getCacheDir( ), fileName );
                  FileOutputStream outputStream = new FileOutputStream( photoFile );
                  IOUtils.copy( inputStream, outputStream ); // 라이브러리 설치
                      //implementation 'commons-io:commons-io:2.4'
                      // 임시용 폴더에 저장해서 사용하자.
    //                //임시파일 생성
    //                File file = createImgCacheFile( );
    //                String cacheFilePath = file.getAbsolutePath( );
    
                  // 압축시킨다. 해상도 낮춰서
                  Bitmap photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
                  OutputStream os;
                  try {
                      os = new FileOutputStream(photoFile);
                      photo.compress(Bitmap.CompressFormat.JPEG, 60, os);
                      os.flush();
                      os.close();
                  } catch (Exception e) {
                      Log.e(getClass().getSimpleName(), "Error writing bitmap", e);
                  }
    
                  imageView.setImageBitmap(photo);
                  imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    //                imageView.setImageBitmap( getBitmapAlbum( imageView, albumUri ) );
    
              } catch ( Exception e ) {
                  e.printStackTrace( );
              }
    
              // 네트워크로 보낸다.
          }
          super.onActivityResult(requestCode, resultCode, data);
      }
    // 화면 가로, 세로 크기를 측정해 원래의 형태로 되돌리는 메서드
      public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
    
          Matrix matrix = new Matrix();
          switch (orientation) {
              case ExifInterface.ORIENTATION_NORMAL:
                  return bitmap;
              case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                  matrix.setScale(-1, 1);
                  break;
              case ExifInterface.ORIENTATION_ROTATE_180:
                  matrix.setRotate(180);
                  break;
              case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                  matrix.setRotate(180);
                  matrix.postScale(-1, 1);
                  break;
              case ExifInterface.ORIENTATION_TRANSPOSE:
                  matrix.setRotate(90);
                  matrix.postScale(-1, 1);
                  break;
              case ExifInterface.ORIENTATION_ROTATE_90:
                  matrix.setRotate(90);
                  break;
              case ExifInterface.ORIENTATION_TRANSVERSE:
                  matrix.setRotate(-90);
                  matrix.postScale(-1, 1);
                  break;
              case ExifInterface.ORIENTATION_ROTATE_270:
                  matrix.setRotate(-90);
                  break;
              default:
                  return bitmap;
          }
          try {
              Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
              bitmap.recycle();
              return bmRotated;
          }
          catch (OutOfMemoryError e) {
              e.printStackTrace();
              return null;
          }
      }
profile
공부해보자

0개의 댓글