Android ACTION_IMAGE_CAPTURE Намерение - PullRequest
       85

Android ACTION_IMAGE_CAPTURE Намерение

160 голосов
/ 16 декабря 2009

Мы пытаемся использовать собственное приложение камеры, чтобы позволить пользователю сделать новый снимок. Это прекрасно работает, если мы пропустим EXTRA_OUTPUT extra и вернем маленькое растровое изображение. Однако, если мы putExtra(EXTRA_OUTPUT,...) намерены до его запуска, все работает, пока вы не нажмете кнопку «ОК» в приложении камеры. Кнопка «ОК» просто ничего не делает. Приложение камеры остается открытым и ничего не блокируется. Мы можем отменить его, но файл никогда не будет записан. Что именно мы должны сделать, чтобы ACTION_IMAGE_CAPTURE записал снимок, сделанный в файл?

Редактировать: Это делается с помощью MediaStore.ACTION_IMAGE_CAPTURE намерения, просто чтобы прояснить

Ответы [ 14 ]

144 голосов
/ 19 декабря 2009

это хорошо документированная ошибка в некоторых версиях Android. то есть, в сборках 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. вы должны вести список. как написано, это возможно для устройств быть прошитым версией Android (скажем, CyanogenMod's builds ) с исправленной ошибкой. если это произойдет, ваш код авария. исправление заключается в использовании всего отпечаток устройства.

50 голосов
/ 20 февраля 2011

Я знаю, что на этот вопрос уже отвечали, но я знаю, что многие люди запутались в этом, поэтому я собираюсь добавить комментарий.

У меня точно такая же проблема произошла на моем 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 и передаю его намерению, а затем кнопка ОК работает, как и ожидалось, и все золотое.

Теперь, когда в приложении камеры нажата кнопка «ОК», файл будет находиться в указанном месте. В этом примере это будет /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Нет необходимости копировать файл в «onActivityResult», как указано выше.

32 голосов
/ 08 мая 2013

Я прошел через несколько стратегий захвата фотографий, и, кажется, всегда найдется случай, платформа или определенные устройства, где некоторые или все вышеперечисленные стратегии потерпят неудачу неожиданным образом. Мне удалось найти стратегию, которая использует приведенный ниже код генерации 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

30 голосов
/ 06 октября 2010

У меня была такая же проблема, когда кнопка OK в приложении камеры ничего не делала, как на эмуляторе, так и на Nexus One.

Проблема исчезла после указания безопасного имени файла без пробелов, без специальных символов в MediaStore.EXTRA_OUTPUT Также, если вы указываете файл, который находится в каталоге, который еще не был создан, вы должны создать это первое. Приложение камеры не делает mkdir для вас.

28 голосов
/ 16 декабря 2009

Рабочий процесс, который вы описываете, должен работать так, как вы его описали. Может помочь, если вы покажете нам код создания Intent. В целом, следующий шаблон должен позволить вам делать то, что вы пытаетесь.

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
    }
  }
}

Обратите внимание, что именно Camera Activity будет создавать и сохранять файл, и на самом деле это не часть вашего приложения, поэтому у него не будет разрешения на запись в папку вашего приложения. Чтобы сохранить файл в папке приложения, создайте временный файл на SD-карте и переместите его в папку приложения в обработчике onActivityResult.

7 голосов
/ 14 декабря 2010

Чтобы прокомментировать приведенный выше комментарий Йенчи, кнопка OK также ничего не сделает, если приложение камеры не может выполнить запись в данный каталог.

Это означает, что вы не можете создать файл в месте, доступном только для записи для вашего приложения (например, что-то под getCacheDir()) Что-то под getExternalFilesDir() должно работать, однако.

Было бы неплохо, если бы приложение камеры печатало сообщение об ошибке в журналах, если оно не может записать по указанному пути EXTRA_OUTPUT, но я не нашел его.

4 голосов
/ 29 июня 2010

, чтобы камера записывала на 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. Это было проверено с Nexus One. URI объявлен в закрытом классе, поэтому в результате активности я могу загрузить изображение из URI в imageView для предварительного просмотра при необходимости.

2 голосов
/ 27 июня 2012

Я рекомендую вам следить за сообщением об обучении Android для захвата фотографии. Они показывают на примере, как делать маленькие и большие фотографии. Вы также можете скачать исходный код с здесь

1 голос
/ 01 мая 2016

Решить эту проблему с помощью кода результата деятельности очень просто. Попробуйте этот метод

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();
   }
}
1 голос
/ 28 марта 2011

У меня была такая же проблема, и я исправил ее следующим образом:

Проблема в том, что при указании файла, к которому имеет доступ только ваше приложение (например, путем вызова 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 работает нормально:)

...