Как получить результат камеры как uri в папке с данными?
Я создаю приложение, в котором я хочу захватить изображение, а затем я хочу отправить это изображение в электронное письмо как вложение.
Я открываю камеру с использованием действия android.provider.MediaStore.ACTION_IMAGE_CAPTURE
намерения, и я передаю Uri файла как параметр EXTRA_OUTPUT
, чтобы вернуть изображение в файл. Это работает отлично, и я могу получить захваченное изображение, если я использую external storage uri
как EXTRA_OUTPUT
, но если я использую папку данных uri, она не работает и камера не закрывается, и все ее кнопки не работают.
Вот мой код для получения результата во внешнем каталоге хранения
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
И этот код предназначен для получения изображения в папке с данными
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
Я знал, что папка с данными недоступна для третьего приложения, поэтому может возникнуть проблема, поэтому я создаю одного поставщика контента для совместного использования файла.
Вот мой контент, предоставляющий класс
public class MyContentProvider extends ContentProvider {
private static final String Tag = RingtonContentProvider.class.getName();
public static final Uri CONTENT_URI = Uri
.parse("content://x.y.z/");
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
static {
MIME_TYPES.put(".mp3", "audio/mp3");
MIME_TYPES.put(".wav", "audio/mp3");
MIME_TYPES.put(".jpg", "image/jpeg");
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), uri.getPath());
if (f.exists()) {
return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
}
throw new FileNotFoundException(uri.getPath());
}
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
throw new RuntimeException("Operation not supported");
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
File file = new File(getContext().getFilesDir(), uri.getPath());
if(file.exists()) file.delete();
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Uri.fromFile(file);
}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
File f = new File(getContext().getFilesDir(), "image1.jpg");
if(f.exists()) f.delete();
f = new File(getContext().getFilesDir(), "image2.jpg");
if(f.exists()) f.delete();
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
}
}
Итак, чтобы использовать этот контент, я использую следующий код для передачи uri активности камеры.
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(i, CAMERA_RESULT);
Теперь, если я передаю URL-адрес другого внешнего каталога хранения, который открывается камерой, но он не закрывается в эмуляторе, а в устройстве камера закрывается, но я не получаю результат.
Я объявил, что это содержимое содержит в файле манифеста
<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />
Также я дал разрешение записать внешнее хранилище, а также для использовать камеру.
Я могу захватить изображение, используя внешнее хранилище, но хочу сохранить изображение в каталоге данных вместо внешнего хранилища, потому что, если внешнее хранилище недоступно, я хочу сделать снимок и отправить почту.
Если я создам контент, я также могу поделиться своим изображением с почтовым приложением.
Если мы не предоставим дополнительные функции с намерением камеры, оно вернет изображение в виде байта [] в результате операции в качестве дополнительных данных, но это предназначено для эскиза, поэтому я не могу получить изображение с высоким разрешением, используя этот способ.
Пожалуйста, загляните в мой вопрос и дайте свои предложения.
Спасибо.
Ответы
Ответ 1
Есть два способа решить эту проблему.
1. Сохранить растровое изображение, которое вы получили из метода onActivityResult
Вы можете запустить камеру с помощью намерения сделать снимок, используя ниже код
Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
После съемки фотографии вы получите растровое изображение в методе onActivityResult.
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Теперь вы можете просто сохранить это растровое изображение во внутреннем хранилище
Примечание. Здесь растровый объект состоит из изображения большого пальца, он не будет иметь изображение с полным разрешением
2. Сохранить растровое изображение непосредственно во внутреннем хранилище с помощью поставщика контента
Здесь мы создадим класс поставщика контента, чтобы разрешить разрешение локального каталога хранилища на активность камеры.
Пример поставщика образцов, как показано ниже
public class MyFileContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse
("content://com.example.camerademo/");
private static final HashMap<String, String> MIME_TYPES =
new HashMap<String, String>();
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
@Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
if(!mFile.exists()) {
mFile.createNewFile();
}
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), "newImage.jpg");
if (f.exists()) {
return (ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_WRITE));
}
throw new FileNotFoundException(uri.getPath());
}
}
После этого вы можете просто использовать URI для перехода к активности камеры, используя приведенный ниже код
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);
Если вы не хотите создавать своего собственного провайдера, вы можете использовать FileProvider из support-library-v4. Для получения подробной справки вы можете заглянуть в этот пост
Ответ 2
Лучшее решение, которое я нашел, это: FileProvider (требуется поддержка-library-v4)
Он использует внутреннее хранилище!
https://developer.android.com/reference/android/support/v4/content/FileProvider.html
-
Определите свой FileProvider в манифесте в элементе приложения:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="your.package.name.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/image_path" />
</provider>
-
Добавить разрешения в явном корневом элементе:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
-
Определите пути изображения, например, res/xml/image_path.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="captured_image" path="your/path/"/>
</paths>
-
Java:
private static final int IMAGE_REQUEST_CODE = 1;
// your authority, must be the same as in your manifest file
private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
4.1 цель захвата изображения:
File path = new File(activity.getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File image = new File(path, "image.jpg");
Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, IMAGE_REQUEST_CODE);
4.2 onActivityResult():
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
File path = new File(getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File imageFile = new File(path, "image.jpg");
// use imageFile to open your image
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
Ответ 3
Назначение вызова для захвата фотографии,
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
Затем возьмем растровое изображение в ActivityResult
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Затем напишите, что во внутреннюю память, см. это
// The openfileOutput() method creates a file on the phone/internal storage in the context of your application
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE);
// Use the compress method on the BitMap object to write image to the OutputStream
bm.compress(CompressFormat.JPEG, 90, fos);
Затем в следующий раз, чтобы прочитать этот файл,
Bitmap bitmap = BitmapFactory.decodeFile(file);
Ответ 4
Сперва сохраните фотографию в вашем внешнем хранилище и попробуйте -
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.imageView = (ImageView)this.findViewById(R.id.imageView1);
Button photoButton = (Button) this.findViewById(R.id.button1);
photoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_REQUEST) {
Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array
// save it in your external storage.
FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));
fo.write(byteArray);
fo.flush();
fo.close();
}
}
Следующая цель -
File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
.setType("image/jpg")
.putExtra(Intent.EXTRA_SUBJECT, "Subject")
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
.putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
Ответ 5
Вы также можете сделать это, не требуя поставщика контента, так как вам понадобится SD-карта, чтобы в любом случае открыть цель захвата изображения. Конечно, вы можете взломать присутствие SD-карты, но не с захватом камеры. Поэтому вы проверяете внешнее хранилище, но нуждаетесь в этом в этот момент.... FYI вы также должны проверить библиотеку урожая, например crop-образ вместо использования native, так как он не поддерживается на всех устройствах.
File mediaStorageDir;
String photoFileName = "photo.jpg";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// page = getArguments().getInt("someInt", 0);
// title = getArguments().getString("someTitle");
// Get safe storage directory for photos
mediaStorageDir = new File(
Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
APP_TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
Log.d(APP_TAG,
"Directory exists: "
+ Environment.getExternalStorageState());
Log.d(APP_TAG, "failed to create directory");
}
}
в коде 'take the pic':
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));
...
public Uri getPhotoFileUri(String fileName) {
return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
+ fileName));
}
Ответ 6
После изучения этой ошибки в течение некоторого времени я заметил, что активность, называемая намерением камеры, перезапускается только тогда, когда у телефона заканчивается память.
поэтому, поскольку активность перезапускается, объект, содержащий путь или Uri, на захваченное изображение обновляется (нумеруется)
поэтому я бы предложил вам поймать/обнаружить нулевой объект в onActivityResult и попросить пользователя освободить место на своем устройстве или перезапустить свой телефон для быстрого временного исправления.