Резюме: сделайте снимок с использованием намерения камеры и отобразите фотографию с правильной ориентацией (работает, надеюсь, на все устройства)
Кажется, это самая простая вещь в мире: сделайте снимок в своем приложении для Android, используя активность камеры по умолчанию. Тем не менее, есть много ошибок, которые покрываются несколькими сообщениями в StackOverflow и в Интернете, поскольку, например, Null Intents передаются обратно, ориентация изображения не правильная или OutOfMemoryErrors.
Я ищу решение, которое позволяет мне
- запустите действие камеры с помощью намерения камеры,
- получить Uri фотографии и
- получить правильную ориентацию фотографии.
Кроме того, я хотел бы, насколько это возможно, избегать конкретной конфигурации устройства (производителя, модели, версии os). Поэтому мне интересно: что это лучший способ достичь этого?
Ответы
Ответ 1
ОБНОВЛЕНИЕ: 2 января 2014 года:
Я очень старался избегать реализации различных стратегий на основе производителя устройства. К сожалению, я не обошел это. Просматривая сотни сообщений и беседуя с несколькими разработчиками, никто не нашел решение, которое работает на всех устройствах без использования специального кода производителя.
После того, как я разместил свое решение здесь на StackOverflow, некоторые разработчики попросили меня опубликовать мой код на github. Итак, вот он: AndroidCameraUtil on github
Код был успешно протестирован на самых разных устройствах с Android API-Level >= 8. Полный список см. в файле Readme на github.
CameraIntentHelperActivity предоставляет основную функциональность, которая также более подробно описана ниже.
Вызов активности камеры по умолчанию:
- для устройств Samsung и Sony: я вызываю действие камеры с вызовом метода startActivityForResult. Я устанавливаю константу CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE. Я НЕ устанавливаю никаких других намерений.
- для всех других устройств: я вызываю действие камеры с вызовом метода startActivityForResult, как и ранее. Однако на этот раз я дополнительно установил намерение дополнительного MediaStore.EXTRA_OUTPUT и предоставил URI, где я хочу, чтобы изображение было сохранено.
В обоих случаях я помню время начала работы камеры.
Результат работы камеры:
- Mediastore: Во-первых, я пытаюсь прочитать фотографию, снятую с MediaStore. Используя mangedQuery в контенте MediaStore, я извлекаю последнее взятое изображение, а также его свойство ориентации и его метку времени. Если я найду изображение, и оно не было принято до того, как было вызвано намерение камеры, это изображение, которое я искал. В противном случае я отклоняю результат и пробую один из следующих способов.
- Intent extra: Во-вторых, я пытаюсь получить изображение Uri from intent.getData() возвращаемого намерения. Если это тоже не удастся, я продолжаю с шага 3.
- По умолчанию фото Uri: Если все вышеперечисленные шаги не работают, я использую изображение Uri, которое я передал в действие камеры.
В этот момент я получил фотографию Uri и ее ориентацию, которые я передал в свою UploadPhotoActivity.
Обработка изображений
Пожалуйста, внимательно ознакомьтесь с моим классом BitmapHelper. Он основан на коде, подробно описанном в в этом учебнике.
Кроме того, метод shrinkBitmap также поворачивает изображение, если требуется, на основе информации о ориентации, извлеченной ранее.
Надеюсь, это поможет некоторым из вас.
Ответ 2
Я тестировал этот код с помощью Sony Xperia Go, Samsung Galaxy SII, Samsung Galaxy SIII mini и Samsung Galaxy Y, который работал на всех устройствах.
Но на LG E400 (2.3.6) он не работал, и вы получаете двойные снимки в галерее. Поэтому я добавил файл manufacturer.contains( "lge" ) в void startCameraIntent(), и он исправил проблему.
if(!(manufacturer.contains("samsung")) && !(manufacturer.contains("sony")) && !(manufacturer.contains("lge"))) {
String filename = System.currentTimeMillis() + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, filename);
cameraPicUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPicUri);
}
Ответ 3
На галактике S3 с CM 10.1 я получаю исключение nullpointer в BitmapHelper:
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
впоследствии моя функция UploadPhotoActivity завершится неудачей:
try {
photo = BitmapHelper.readBitmap(this, cameraPicUri);
if (photo != null) {
photo = BitmapHelper.shrinkBitmap(photo, 600, rotateXDegrees);
thumbnail = BitmapHelper.shrinkBitmap(photo, 100);
ImageView imageView = (ImageView) findViewById(R.id.sustainable_action_photo);
imageView.setImageBitmap(photo);
} else {
Log.e(TAG,"IMAGE ERROR 1");
}
} catch (Exception e) {
Log.e(TAG,"IMAGE ERROR 2");
e.printStackTrace();
}
во втором журнале (ОШИБКА ИЗОБРАЖЕНИЯ 2).
После нескольких попыток моя камера сломалась, и я получил "Не могу подключиться к камере" -error.
Протестировал его на связке 7 и отлично работает.
Изменить: сузилось до этого:
fileDescriptor = context.getContentResolver(). openAssetFileDescriptor (selectedImage, "r" );
Хотя selectedImage содержит это:
Файл:///storage/emulated/0/DCIM/Camera/IMG_20131023_183343.jpg
ФайлDescriptor возвращает исключение FileNotFoundException. Я проверил файловую систему, и изображение не сохраняется в этом месте. CameraPicUri в TakePhotoActivity указывает на несуществующий образ. Я в настоящее время проверяю, где все идет не так.
Edit2: Я понял ошибку: поскольку устройство является Samsung и сообщает App, что это устройство Samsung, применяются ваши конкретные исправления для вашего Samsung. Однако Cyanogenmod не нуждается в этих исправлениях, и, в конце концов, код ломается. Как только вы удалите
(manufacturer.contains( "samsung" )) &&
Это работает. Поскольку это пользовательский ПЗУ, вы не могли планировать это, конечно. Я пытаюсь найти способ определить, работает ли устройство cyanogenmod, а затем включить его в свой код.
Спасибо за хорошее исправление камеры!
Edit3: Я исправил его для запуска на Cyanogenmod на Galaxy S3, изменив код на это:
Ну, теперь это иногда работает, иногда это не так. Странно.
if (getPackageManager(). hasSystemFeature ( "com.cyanogenmod.android" ) || (! (производитель .contains( "samsung" )) & &! (manufacturer.contains( "sony" )) & &! (manufacturer.contains( "lge" ))))
Ответ 4
Я испытываю некоторые проблемы при использовании этого с Sony Xperia Z5.
Я добавил это, и это стало намного лучше.
if (buildType.contains("sony")&& buildDevice.contains("e5823")) {
setPreDefinedCameraUri = true;}
Но в 4 раза из 22 он перезапустил камеру и один раз перезапустил ее два раза. Я перезапустил приложение из-за каждого теста.
Есть ли способ обойти это или я принимаю этот результат?
Дело в том, что если камера перезапустится, я могу дважды нажать кнопку "Назад" и стрела, изображение будет в моем изображении и сохранено