-
Android 7.0 (Nougat) 카메라(ACTION_IMAGE_CAPTURE) 및 사진 자르기(Crop) 구현Android 2019. 4. 1. 14:41
Nougat 안드로이드 7.0 버전에서 변경된 사항들이 있다. 개인 파일 보안 강화를 위해 Android 7.0 이상을 대상으로 하는 앱의 개인 디렉토리는 액세스가 제한된다. 앱 외부에서 file:// URI의 노출을 금지하는 StrictMode API정책을 적용하여, 파일 URI를 포함하는 인텐트가 앱을 통해 보내지면 FileUriException이 발생한다
애플리케이션 간에 파일을 공유하려면 content:// URI를 보내고 이 URI에 대해 임시 액세스 권한을 부여해야한다. 이 권한을 가장 쉽게 부여하는 방법은 FileProvider 클래스를 사용하는 방법이 있다.
Uri.fromFile()의 리턴형인 file://을 사용하지말고 FileProvider.getUriForFile()의 리턴형인 content://로 받아서 이경로를 인텐트에 전달해야한다.
-기존 카메라를 호출하던 소스
12345Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);tempImageFile = new File(Environment.getExternalStorageDirectory(),"tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg");intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempImageFile));startActivityForResult(intent, Definitions.PICK_FROM_CAMERA);- 변경한 카메라 호출하는 소스
1234Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri providerURI = FileProvider.getUriForFile( context ,getPackageName() , photoFile);intent.putExtra(MediaStore.EXTRA_OUTPUT , providerURI);startActivityForResult(intent, Definitions.PICK_FROM_CAMERA);** onActivityResult에서 data값을 받아 getBitmapFromData(data)로 받아낸 bitamp은 썸네일 이미지이다.
그대로 이용하려면 화질이 많이 떨어진다. Full 이미지를 받으려면 파일로 저장을 하여 읽어 와야한다.
1234567@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {Bundle extras = data.getExtras();}}이제 Nougat( 안드로이드 7.0 )에서 카메라 구동과 사진자르기(crop)을 위한 소스를 살펴보자.
1. Manifest에 권한 및 provider 추가
1234<manifest ...><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /></manifest>Colored by Color Scripter12345678910111213<application>...<providerandroid:name="android.support.v4.content.FileProvider"android:authorities="com.example.android.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths"></meta-data></provider>...</application>Colored by Color Scripter2. file_paths.xml
FileProvider는 미리 지정한 디렉토리에 위치할 파일에 대한 content URI만 생성할 수 있다.
디렉토리를 지정하기 위해 <paths> element를 이용해 path와 저장 영역을 명시해야한다.
name element
- 보안을 강화를 위해 공유하는 하위 디렉토리 이름을 감춘다.
path element
- 파일을 공유하는 실제 하위 디렉토리
<files-path> : 내부저장소 - Context.getFilesDir()
<cache-path> : 내부저장소 - getCacheDir()
<external-path> : 외부저장소 - Environment.getExternalStorageDirectory()
<external-files-path> : 외부저장소 - Context.getExternalFilesDir()
<external-cache-path> : 외부저장소 - Context.getExternalCacheDir()
1234<?xml version="1.0" encoding="utf-8"?><external-path name="hidden" path="/Pictures/pic"/></paths>3. Activty.java - 카메라 구동 captureCamera() & createImageFile()
123456789101112131415161718private static Uri imageUri;private File createImageFile() throws IOException {// Create an image file nameString timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());String imageFileName = "JPEG_" + timeStamp + ".jpg";File imageFile = null;File storageDir = new File(Environment.getExternalStorageDirectory()+"/Pictures" , "pic");}imageFile = new File(storageDir, imageFileName);return imageFile;}12345678910111213141516171819202122232425262728293031323334//카메라 작동private void captureCamera(){String state = Environment.getExternalStorageState();//외장메모리 검사if(Environment.MEDIA_MOUNTED.equals(state)){Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if(intent.resolveActivity( getPackageManager()) != null){File photoFile = null;try{photoFile = createImageFile();}catch(IOException e){}if(photoFile != null){//getUriForFile 의 두번째 인자는 매니패스트 provider의 authorites와 일치해야한다Uri providerURI = FileProvider.getUriForFile( context , getPackageName() , photoFile);imageUri = providerURI;//인텐트 전달 할때는 FileProvider의 return값인 Content://로만 , providerURI 값에 카메라 데이터를 넣어보냄intent.putExtra(MediaStore.EXTRA_OUTPUT , providerURI);startActivityForResult(intent, Definitions.PICK_FROM_CAMERA);}}}else{return;}}4. Activty.java - 사진 자르기(Crop) onActivityResult() & cropImage()
1234567891011121314public void onActivityResult(Integer requestCode, Integer resultCode, Intent data) {if (requestCode == Definitions.PICK_FROM_CAMERA) {//사진 자르기cropImage();}else if (requestCode == Definitions.CROP_FROM_CAMERA) {try {Bitmap photo = getBitmapFromUri(imageUri);} catch (Exception e) {}}}** getUriforFile()이 return한 content URI에 대한 임시 접근권한을 승인하려면 grantUriPermission을 호출한다.
1234567891011121314151617181920212223//사진 자르기public void cropImage(){intent.setDataAndType(imageUri , "image/*");intent.putExtra("aspectX", 1);intent.putExtra("aspectY", 1);intent.putExtra("scale", true);intent.putExtra("output", imageUri);/*** getUriforFile()이 return한 content URI에 대한 접근권한을 승인하려면 grantUriPermission을 호출한다.* mode_flags 파라미터의 값에 따라. 지정한 패키지에 대해 content URI를 위한 임시 접근을 승인한다.* 권한은 기기가 리부팅 되거나 revokeUriPermission()을 호출하여 취소할때까지 유지.**/intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);grantUriPermission( getPackageName(), imageUri , Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);startActivityForResult(intent, Definitions.CROP_FROM_CAMERA);}캐시영역으로 저장한 후 삭제가 필요하다면 아래 게시글을 확인하여 참고하면 된다.
'Android' 카테고리의 다른 글
Android 64비트 대응하기 - ARM , X86, MIPS (1) 2019.04.08 Android 64비트 지원 대응하기 (0) 2019.04.08 Android 카메라 사진 캐시영역(Cache-path) 저장하기 (0) 2019.04.05 Android FCM 구현하기(GCM에서 전환) - 1/2 FCM콘솔 (0) 2019.04.01 Android FCM 구현하기(GCM에서 전환) - 2/2 소스구현 (0) 2019.04.01 댓글