studio programacion herramientas fundamentos con avanzado aplicaciones android android-camera android-contentprovider

android - programacion - ¿Cómo obtener el resultado de la cámara como un uri en la carpeta de datos?



manual de android en pdf (6)

Estoy creando una aplicación en la que quiero capturar una imagen y luego deseo enviar esa imagen en el correo electrónico como un archivo adjunto.

Estoy abriendo una cámara con android.provider.MediaStore.ACTION_IMAGE_CAPTURE acción de intención y estoy pasando el Uri del archivo como un parámetro EXTRA_OUTPUT para devolver la imagen al archivo. Esto funciona perfectamente y puedo obtener la imagen capturada si utilizo el external storage uri como EXTRA_OUTPUT pero si utilizo la carpeta de datos uri no está funcionando y la cámara no se está cerrando y sus botones no funcionan.

Aquí está mi código para obtener el resultado en el directorio de almacenamiento externo

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);

Y este código es para obtener la imagen en la carpeta de datos

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);

Sabía que la tercera aplicación no puede acceder a la carpeta de datos, por lo que puede haber un problema así que tengo que crear un proveedor de contenido para compartir el archivo.

Aquí está mi contenido proporcionar clase

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

Entonces, para usar este contenido, estoy usando el siguiente código para pasar el uri a la actividad de la cámara

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);

Ahora si paso la url otro directorio de almacenamiento externo la cámara se está abriendo pero no se está cerrando en el emulador pero en el dispositivo la cámara se cerrará pero no obtendré el resultado.

He declarado este contenido en el archivo de manifiesto

<provider android:name=".contentproviders.MyContentProvider" android:authorities="x.y.z" />

También he dado permiso para escribir el almacenamiento externo y también para usar la cámara .

Puedo capturar la imagen usando el almacenamiento externo, pero quiero almacenar la imagen en el directorio de datos en lugar de almacenamiento externo porque si el almacenamiento externo no está disponible, quiero capturar la imagen y enviar mensajes.

Si creo contenido, también puedo compartir mi imagen en la aplicación de correo electrónico.

Si no proporcionamos los extras con la intención de la cámara, devolverá la imagen como un byte [] en el resultado de la actividad como un extra de datos, pero esto es para la miniatura, por lo que no puedo obtener la imagen de alta resolución de esta manera. .


Al principio, guarde la foto en su almacenamiento externo y pruébelo.

@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(); } }

Siguiente objetivo -

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);


Después de investigar este error por un tiempo, me di cuenta de que la actividad que llamaba la intención de la cámara solo se reinicia cuando el teléfono se está quedando sin memoria. así que debido a que la actividad se reinicia, el objeto que contiene la ruta o Uri a la imagen capturada se actualiza (nulled)

por lo que le sugiero que capture / detecte el objeto nulo en onActivityResult, y le pida al usuario que libere espacio en su dispositivo o que reinicie su teléfono para una solución temporal rápida.


Hay dos formas de resolver este problema.

1. Guarde el mapa de bits que recibió del método OnActivityResult

Puede iniciar la cámara a través de la intención de capturar fotos usando el código siguiente

Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST);

Después de la foto de captura, obtendrás un mapa de bits en el método de respuesta de actividad

if (requestCode == CAMERA_REQUEST) { Bitmap photo = (Bitmap) data.getExtras().get("data"); }

Ahora puedes simplemente guardar este mapa de bits en el almacenamiento interno

Nota: Aquí el objeto de mapa de bits consiste en una imagen de pulgar, no tendrá una imagen de resolución completa

2. Guarde el mapa de bits directamente en el almacenamiento interno usando el proveedor de contenido

Aquí crearemos una clase de proveedor de contenido para permitir el permiso del directorio de almacenamiento local a la actividad de la cámara

Ejemplo de proveedor de muestra como por debajo

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()); } }

Después de eso, simplemente puede usar el URI para pasar a la actividad de la cámara utilizando el siguiente código

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI); startActivityForResult(i, CAMERA_RESULT);

Si no desea crear su propio proveedor, puede usar FileProvider desde support-library-v4. Para obtener ayuda detallada, puede consultar esta publicación


Intención de llamar para capturar una foto,

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST);

A continuación, tome Bitmap en ActivityResult

if (requestCode == CAMERA_REQUEST) { Bitmap photo = (Bitmap) data.getExtras().get("data"); }

Luego, escribe eso en la memoria interna, mira esto

// 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);

Luego, la próxima vez para leer ese archivo,

Bitmap bitmap = BitmapFactory.decodeFile(file);


La mejor solución que encontré es: FileProvider (necesita soporte-biblioteca-v4) ¡Utiliza el almacenamiento interno! FileProvider

  1. Defina su FileProvider en Manifiesto en el elemento Aplicación:

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

  2. Agregar permisos en el elemento raíz del manifiesto:

    <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />

  3. Defina sus rutas de imagen en, por ejemplo, res / xml / image_path.xml:

    <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="captured_image" path="your/path/"/> </paths>

  4. 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 intento de captura:

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


También puede hacer esto sin necesidad de un proveedor de contenido ya que necesitará la tarjeta sd para abrir el intento de captura de imagen de la cámara de todos modos. Por supuesto, puede piratear la presencia de la tarjeta sd pero no con la captura de la intención de la cámara ... Por lo tanto, está buscando almacenamiento externo pero lo necesita en este punto ... FYI también debería consultar una biblioteca de cultivos como crop-image en lugar de utilizar native, ya que no es compatible con todos los dispositivos.

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"); } }

en su código ''tomar la foto'':

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