Android ACTION_IMAGE_CAPTURE 인 텐트


163

기본 카메라 앱을 사용하여 사용자가 새로운 사진을 찍을 수 있도록 노력하고 있습니다. EXTRA_OUTPUT extra작은 비트 맵 이미지를 제외 하고 반환하면 정상적으로 작동 합니다. 그러나 putExtra(EXTRA_OUTPUT,...)시작하기 전에 의도에 따라 카메라 앱에서 "확인"버튼을 누르려고 할 때까지 모든 것이 작동합니다. "확인"버튼은 아무 것도 수행하지 않습니다. 카메라 앱이 열려 있고 아무것도 잠기지 않습니다. 우리는 그것을 취소 할 수 있지만 파일은 결코 쓰여지지 않습니다. 우리가 정확히 무엇을해야합니까ACTION_IMAGE_CAPTURE촬영 한 사진을 파일로 쓰 합니까?

편집 : 이것은 MediaStore.ACTION_IMAGE_CAPTURE분명하게하기 위해 의도 를 통해 수행됩니다.

답변:


144

이것은 일부 안드로이드 버전에서 잘 알려진 버그 입니다. 즉, Google의 Android 빌드에서 이미지 캡처가 문서화 된대로 작동하지 않습니다. 내가 일반적으로 사용한 것은 유틸리티 클래스에서 이와 같은 것입니다.

public boolean hasImageCaptureBug() {

    // list of known devices that have the bug
    ArrayList<String> devices = new ArrayList<String>();
    devices.add("android-devphone1/dream_devphone/dream");
    devices.add("generic/sdk/generic");
    devices.add("vodafone/vfpioneer/sapphire");
    devices.add("tmobile/kila/dream");
    devices.add("verizon/voles/sholes");
    devices.add("google_ion/google_ion/sapphire");

    return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
            + android.os.Build.DEVICE);

}

그런 다음 이미지 캡처를 시작할 때 버그를 확인하는 의도를 만듭니다.

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

그런 다음 내가 돌아온 활동에서 장치에 따라 다른 일을합니다.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
     switch (requestCode) {
         case GlobalConstants.IMAGE_CAPTURE:
             Uri u;
             if (hasImageCaptureBug()) {
                 File fi = new File("/sdcard/tmp");
                 try {
                     u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
                     if (!fi.delete()) {
                         Log.i("logMarker", "Failed to delete " + fi);
                     }
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 }
             } else {
                u = intent.getData();
            }
    }

이렇게하면 새로운 카메라 앱을 작성하지 않아도되지만이 코드도 좋지 않습니다. 큰 문제는

  1. 버그가있는 장치에서 전체 크기의 이미지를 얻지 못합니다. 이미지 내용 제공자에 삽입 된 너비가 512 픽셀 인 사진을 얻습니다. 버그가없는 장치에서는 모든 것이 문서로 작동하므로 큰 그림이 나타납니다.

  2. 리스트를 유지해야합니다. 작성된 것처럼 버그가 수정 된 안드로이드 버전 ( 시아 노겐 모드 빌드 )으로 장치를 플래시 할 수 있습니다 . 이 경우 코드가 중단됩니다. 수정은 전체 장치 지문을 사용하는 것입니다.


21
Galaxy S의 경우 : intent.getData ()는 null을 반환하고 Galaxys의 카메라 앱은 자체적으로 갤러리에 새 사진을 삽입하지만 uri는 반환하지 않습니다. 따라서 파일의 사진을 갤러리에 삽입하면 사진이 복제됩니다.
Arseniy 2016 년

1
이봐 내 장치가 여기에 나열되지 않고 ur 방식으로 구현했지만 내 응용 프로그램이 여전히 충돌 이유를 알 수 없습니다. 도와주세요
Geetanjali

droid-x 및 sony xpheria 장치에서이 코드를 사용합니다. 두 장치 모두 의도 값을 null로 반환합니다. 또한 droidx는 결과 코드를 0으로 반환하고 xpheria는 결과 코드를 -1로 반환합니다. 왜 그런지 알아야합니다.
rOrlig

5
yanokwa의 답변 시작 부분에있는 "잘 문서화 된 버그"에 대한 링크가 올바르지 않습니다. 그 버그없이 카메라 응용 프로그램을 호출 관한 putExtra (... EXTRA_OUTPUT)
프랭크 하퍼

code.google.com/p/android/issues/detail?id=1480 글쎄, 그럼 어떻게 설명하면 되나요?
Pencilcheck

50

나는 이것이 전에 대답되었다는 것을 알고 있지만 많은 사람들이 이것에 걸려 넘어지는 것을 알고 있으므로 의견을 추가 할 것입니다.

Nexus One에서도 이와 동일한 문제가 발생했습니다. 카메라 앱이 시작되기 전에 디스크에없는 파일에서 가져온 것입니다. 따라서 카메라 앱을 시작하기 전에 기존 파일이 있는지 확인했습니다. 내가 사용한 샘플 코드는 다음과 같습니다.

String storageState = Environment.getExternalStorageState();
        if(storageState.equals(Environment.MEDIA_MOUNTED)) {

            String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
            _photoFile = new File(path);
            try {
                if(_photoFile.exists() == false) {
                    _photoFile.getParentFile().mkdirs();
                    _photoFile.createNewFile();
                }

            } catch (IOException e) {
                Log.e(TAG, "Could not create file.", e);
            }
            Log.i(TAG, path);

            _fileUri = Uri.fromFile(_photoFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
            intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
            startActivityForResult(intent, TAKE_PICTURE);
        }   else {
            new AlertDialog.Builder(MainActivity.this)
            .setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
            .setCancelable(true).create().show();
        }

먼저 MD5 해시를 사용하여 고유 한 파일 이름을 만들어 적절한 폴더에 넣습니다. 그런 다음 존재하는지 확인합니다 (그렇지 않지만 어쨌든 확인하는 것이 좋습니다). 존재하지 않으면 부모 디렉토리 (폴더)를 가져 와서 폴더 계층을 만듭니다 (따라서 파일 위치로 이어지는 폴더가 존재하지 않으면이 줄 다음에 나타납니다. 파일을 만들면 Uri를 가져 와서 의도에 전달한 다음 OK 버튼이 예상대로 작동하고 모두 황금색입니다.

이제 카메라 앱에서 Ok 버튼을 누르면 파일이 지정된 위치에 나타납니다. 이 예에서는 /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg입니다.

위에 게시 된 "onActivityResult"에 파일을 복사 할 필요가 없습니다.


13
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />Android 1.5보다 최신 버전을 사용하는 경우 매니페스트에 있는지 확인하십시오 . 카메라를 디버깅하는 데 몇 시간이 걸리고 포함되지 않았으므로 저장이 항상 실패합니다.
swanson

이것은 나에게 효과적이지만 약 10 개의 이미지마다 빈 파일을 덮어 쓰지 않고 이미지가 있어야하는 0 바이트 파일로 끝납니다. 매우 성가 시며 추적하기가 어려웠습니다.
joey_g216

2
젤리 빈이있는 nexus 7과 같은 일부 장치에서는 .getName () 대신 .getAbsolutePath ()를 사용하도록 Environment.getExternalStorageDirectory (). getName ()을 변경해야합니다.
Keith

35

나는 많은 사진 캡처 전략을 겪어 왔으며 항상 위의 전략 중 일부 또는 전부가 예기치 않은 방식으로 실패하는 경우, 플랫폼 또는 특정 장치가있는 것처럼 보입니다. 아래의 URI 생성 코드를 사용하는 전략을 찾을 수 있었는데 모든 경우에 해당되는 것은 아니지만 대부분의 경우 작동합니다.

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

토론에 더 기여하고 이민자를 돕기 위해 사진 캡처 구현을위한 여러 가지 전략을 보여주는 샘플 / 테스트 앱을 만들었습니다. 다른 구현의 기여는 토론에 추가하도록 권장됩니다.

https://github.com/deepwinter/AndroidCameraTester


대단 했어. 나는 아직 내 모든 장치에서 이것을 테스트하지는 않았지만, 당신이 이미 그렇게 많이했다고 가정합니다. 이 주제에는 많은 소음이 있으며,이 특정 답변은 매우 간단하고 깨끗합니다. 지금까지 Nexus 5에서 작동합니다. EXTRA_OUTPUT없이 ACTION_IMAGE_CAPTURE가 작동하지 않았다는 보고서가 처음으로 많이 나타났습니다. 나는 이것이 전반적으로 작동하기를 희망하지만, 지금까지는 좋았다. Thx
Rich

1
@deepwinter-감사합니다. 나는 머리카락을 뽑았지만 콘텐츠 해결 솔루션은 내가 가진 모든 문제 시나리오에서 잘 작동합니다.
Billy Coover

이 파티에 늦었지만 이것은 나를 위해 일하는 유일한 해결책이었습니다. 무리 감사!
StackJP

어떻게 든 MediaStore를 검색 할 수 onActivityResult있습니까? EXTRA_OUTPUT 또는 직접 기억해야합니까? 사진을 찍는 동안 앱이 파괴 된 경우에도 앱이 기억할 수 있도록 지속적으로 저장해야합니다.
prom85

그것은 lolipop와 삼성 갤럭시 노트 3에 대한 작업을하지 않습니다 ava.lang.NullPointerException가 : 널 객체 참조에 ') java.lang.String의 android.net.Uri.getScheme ('가상 메소드를 호출하려고합니다
이고르 얀코비치

30

카메라 앱의 확인 버튼이 에뮬레이터와 넥서스 모두에서 아무것도하지 않는 것과 같은 문제가있었습니다.

공백이없고 특수 문자가없는 안전한 파일 이름을 지정한 후에도 문제가 해결되었습니다. MediaStore.EXTRA_OUTPUT 또한 아직 작성되지 않은 디렉토리에있는 파일을 지정하는 경우 먼저 작성해야합니다. 카메라 앱은 mkdir을 수행하지 않습니다.


1
경로에 둘 이상의 디렉토리 레벨이 있고 모든 디렉토리 레벨을 작성하려는 경우 mkdirs()대신에 사용하십시오 mkdir()( mkdir마지막 디렉토리 인 '리프'디렉토리 만 작성). 나는 그것에 약간의 문제가 있었고, 이 대답 은 나를 도왔다.
Diana

간단한 타임 스탬프를 사용할 수 있습니까 ?? 또는 일부 오류를 만들 수 있습니까 ?? :) 대답 할 때 행복은 태그 나 할
Rakeeb Rajbhandari을

@ user2247689 "단순 타임 스탬프"로 생성 한 파일 이름은 확실하지 않지만 SD 카드의 FAT 형식에서 허용되지 않는 특수 문자를 포함하지 않는 한 괜찮습니다.
Yenchi

28

설명하는 워크 플로는 설명 된대로 작동해야합니다. 인 텐트 생성 관련 코드를 보여 주면 도움이 될 수 있습니다. 일반적으로 다음 패턴을 사용하면 원하는 작업을 수행 할 수 있습니다.

private void saveFullImage() {
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
  outputFileUri = Uri.fromFile(file);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
  startActivityForResult(intent, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
    // Check if the result includes a thumbnail Bitmap
    if (data == null) {    
      // TODO Do something with the full image stored
      // in outputFileUri. Perhaps copying it to the app folder
    }
  }
}

파일을 만들고 저장하는 것은 카메라 활동이며 실제로는 응용 프로그램의 일부가 아니므로 응용 프로그램 폴더에 대한 쓰기 권한이 없습니다. 파일을 앱 폴더에 저장하려면 SD 카드에 임시 파일을 만들어 onActivityResult처리기 의 앱 폴더로 옮깁니다 .


이 / should / 작동하지만 카메라 앱의 확인 버튼은 아무것도하지 않습니다. 우리는 SD 카드에 쓰기 작업을 수행 했으므로 파일 권한 문제라고 생각합니다 (응용 프로그램에 자동으로 개인 디렉토리에 대한 쓰기 권한이 있다고 생각했지만). 도와 주셔서 감사합니다!
Drew

1
앱에 개인 디렉터리에 대한 쓰기 권한이 있습니다. 사진을 찍는 카메라 앱은 그렇지 않습니다. 앱 내에서 onActivityResult의 파일을 이동할 수 있습니다. 또는 카메라 활동을 직접 구현할 수 있지만 과도하게 보입니다.
Reto Meier

카메라 앱을 다시 실행하는 것은 일반적이지만 지루한 접근 방법 인 것 같습니다 ... getDir ( "images", MODE_WORLD_WRITEABLE)을 사용할 때 해당 디렉토리에서 생성하도록 파일에 적용되지 않는 권한이 있습니까?
Drew

반드시 그런 것은 아닙니다. 폴더 내 파일에 대한 권한은 아니지만 폴더에 대한 권한입니다.
Reto Meier

Android 2.2 이상이 설치된 Android 장치에서 코드를 테스트했으며 모두 제공된 경로에 이미지를 저장했습니다. 이미지를 얻으려면 이것이 표준 동작이어야한다고 생각합니다.
Janusz

7

위의 Yenchi의 의견을 추적하기 위해 카메라 앱이 문제의 디렉토리에 쓸 수없는 경우 OK 버튼도 아무런 작업을 수행하지 않습니다.

즉, 응용 프로그램에서만 쓸 수있는 위치에 파일을 만들 수 없습니다 (예 getCacheDir()):getExternalFilesDir() 작동해야 함).

지정된 EXTRA_OUTPUT경로에 쓸 수 없다면 카메라 앱에서 오류 메시지를 로그에 인쇄하면 좋을 것입니다.


4

카메라가 sdcard에 쓰도록하지만 갤러리 응용 프로그램의 새 앨범에 보관하려면 다음을 사용하십시오.

 File imageDirectory = new File("/sdcard/signifio");
          String path = imageDirectory.toString().toLowerCase();
           String name = imageDirectory.getName().toLowerCase();


            ContentValues values = new ContentValues(); 
            values.put(Media.TITLE, "Image"); 
            values.put(Images.Media.BUCKET_ID, path.hashCode());
            values.put(Images.Media.BUCKET_DISPLAY_NAME,name);

            values.put(Images.Media.MIME_TYPE, "image/jpeg");
            values.put(Media.DESCRIPTION, "Image capture by camera");
           values.put("_data", "/sdcard/signifio/1111.jpg");
         uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI , values);
            Intent i = new Intent("android.media.action.IMAGE_CAPTURE"); 

            i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

            startActivityForResult(i, 0); 

매번 고유 한 파일 이름을 생성하고 내가 작성한 1111.jpg를 교체해야합니다. 이것은 넥서스 1로 테스트되었습니다. uri는 private 클래스에 선언되어 있으므로 활동 결과에서 필요한 경우 미리보기를 위해 uri에서 imageView로 이미지를로드 할 수 있습니다.


"_data"열은 어디에서 왔습니까? 상수가 없습니까?
Matthias

3
아, 그것은 developer.android.com/reference/android/provider/… 코드에서 마술 문자열을 사용해서는 안됩니다.
Matthias

1

나는 같은 문제가 있었고 다음과 같이 수정했습니다.

문제는 앱에서만 액세스 할 수있는 파일을 지정하면 (예 : getFileStreamPath("file"); )

그래서 나는 주어진 파일이 실제로 존재하고 모든 사람이 그것에 대한 쓰기 권한을 가지고 있는지 확인했습니다.

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);

이런 식으로 카메라 앱은 주어진 Uri에 대한 쓰기 권한을 가지며 OK 버튼은 정상적으로 작동합니다. :)


AndroidRuntime (2.1 에뮬레이터)에서 NoSuchMethodError 발생 : java.io.File.setWritable
jwadsack

1

사진을 찍기 위해 안드로이드 훈련 게시물을 따르는 것이 좋습니다. 예를 들어 작고 큰 사진을 찍는 방법을 보여줍니다. 여기 에서 소스 코드를 다운로드 할 수도 있습니다


0

나는 다른 소스 (갤러리, 카메라)에서 이미지를 선택하고 관리 할 수있는 간단한 라이브러리를 만들었습니다. 어딘가에 저장하고 (SD 카드 또는 내부 메모리) 이미지를 다시 반환하므로 자유롭게 사용하고 개선하십시오 -Android-ImageChooser .


귀하의 링크가 더 이상 작동하지 않습니다 !! 왜, 나는 그것을 2 주 전에 보았고 그것을 사용하고 싶었습니다 :(
Mohammad Ersan

0

Praveen이 지적했듯이 카메라에서 파일을 쓸 수 있어야합니다 .

내 사용법에서 파일을 내부 저장소에 저장하고 싶었습니다. 나는 이것을했다 :

Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
    try{
        cacheFile = createTempFile("img",".jpg",getCacheDir());
        cacheFile.setWritavle(true,false);
    }catch(IOException e){}
    if(cacheFile != null){
        i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
        startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
    }
}

다음 cacheFile은 작성된 파일을 나타내는 데 사용되는 전역 파일입니다. 그런 다음 결과 메소드에서 리턴 된 의도는 널입니다. 그런 다음 의도 처리 방법은 다음과 같습니다.

protected void onActivityResult(int requestCode,int resultCode,Intent data){
    if(requestCode != RESULT_OK){
        return;
    }
    if(requestCode == REQUEST_IMAGE_CAPTURE){
        try{
            File output = getImageFile();
            if(output != null && cacheFile != null){
                copyFile(cacheFile,output);

                //Process image file stored at output

                cacheFile.delete();
                cacheFile=null;
            }
        }catch(IOException e){}
    }
}

다음 getImageFile()은 이미지를 저장할 파일의 이름을 지정하고 작성하는 유틸리티 방법이며 파일 copyFile()을 복사하는 방법입니다.


0

이 코드를 사용할 수 있습니다

private Intent getCameraIntent() {

    PackageManager packageManager = mContext.getPackageManager();
    List<ApplicationInfo> list = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
    Intent main = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    List<ResolveInfo> launchables = packageManager.queryIntentActivities(main, 0);
    if (launchables.size() == 1)
        return packageManager.getLaunchIntentForPackage(launchables.get(0).activityInfo.packageName);
    else
        for (int n = 0; n < list.size(); n++) {
            if ((list.get(n).flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                Log.d("TAG", "Installed Applications  : " + list.get(n).loadLabel(packageManager).toString());
                Log.d("TAG", "package name  : " + list.get(n).packageName);
                String defaultCameraPackage = list.get(n).packageName;


                if (launchables.size() > 1)
                    for (int i = 0; i < launchables.size(); i++) {
                        if (defaultCameraPackage.equals(launchables.get(i).activityInfo.packageName)) {
                            return packageManager.getLaunchIntentForPackage(defaultCameraPackage);
                        }
                    }
            }
        }
    return null;
}

0

활동 결과 코드로이 문제를 해결하는 것은 매우 간단합니다.이 방법을 사용해보십시오.

if (reqCode == RECORD_VIDEO) {
   if(resCode == RESULT_OK) {
       if (uri != null) {
           compress();
       }
    } else if(resCode == RESULT_CANCELED && data!=null){
       Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
   }
}

위의 불완전한 스 니펫은 그것이 주장하는 단순성을 가치가 없습니다. 낯선 사람에게는 분명히 숨겨진 복잡성이 있습니다. 파일 유무 스캔 등. 시들어와 같이 공개적으로 사용 가능하거나 앱 전용입니다. 공급자가 필요하거나 썸네일 전용 크기를 얻는 등. 이것은 조심하지 않은 여행자가 알아야 할 정보 격차입니다.
truthadjustr

0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_CANCELED)
    {
        //do not process data, I use return; to resume activity calling camera intent
        enter code here
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.