update studio proveedor open definicion contentprovider content contenido chooser android android-intent android-gallery android-contentresolver

android - studio - proveedor de contenido video



La galería de Android en Android 4.4(KitKat) devuelve un URI diferente para Intent.ACTION_GET_CONTENT (19)

Antes de KitKat (o antes de la nueva Galería), el Intent.ACTION_GET_CONTENT devolvió un URI como este

contenido: // media / external / images / media / 3951.

Usando ContentResolver y ContentResolver por MediaStore.Images.Media.DATA devolvió la URL del archivo.

Sin embargo, en KitKat, la Galería devuelve un URI (a través de "Último") como este:

contenido: //com.android.providers.media.documents/document/image: 3951

¿Cómo manejo esto?


Esto es lo que hago:

Uri selectedImageURI = data.getData(); imageFile = new File(getRealPathFromURI(selectedImageURI)); private String getRealPathFromURI(Uri contentURI) { Cursor cursor = getContentResolver().query(contentURI, null, null, null, null); if (cursor == null) { // Source is Dropbox or other similar local file path return contentURI.getPath(); } else { cursor.moveToFirst(); int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); return cursor.getString(idx); } }

NOTA: el método managedQuery() está en desuso, así que no lo estoy usando.

Esta respuesta es de m3n0R en la pregunta de Android get real path por Uri.getPath () y no reclamo crédito. Simplemente pensé que las personas que aún no han resuelto este problema podrían usar esto.


Creo que las respuestas ya publicadas deberían hacer que la gente vaya en la dirección correcta. Sin embargo, aquí está lo que hice que tenía sentido para el código heredado que estaba actualizando. El código heredado estaba usando el URI de la galería para cambiar y luego guardar las imágenes.

Antes de 4.4 (y google drive), los URI se verían así: contenido: // media / external / images / media / 41

Como se indica en la pregunta, con más frecuencia se ven así: contenido: //com.android.providers.media.documents/document/image: 3951

Como necesitaba la capacidad de guardar imágenes y no alterar el código ya existente, simplemente copié el URI de la galería en la carpeta de datos de la aplicación. Luego originó un nuevo URI del archivo de imagen guardado en la carpeta de datos.

Aquí está la idea:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent), CHOOSE_IMAGE_REQUEST); public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image"); //Copy URI contents into temporary file. try { tempFile.createNewFile(); copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile)); } catch (IOException e) { //Log Error } //Now fetch the new URI Uri newUri = Uri.fromFile(tempFile); /* Use new URI object just like you used to */ }

Nota: copyAndClose () solo realiza la E / S de archivos para copiar InputStream en un FileOutputStream. El código no está publicado.


Esta biblioteca de Android maneja los cambios de caso en KitKat (incluidas las versiones anteriores - 2.1+):
https://github.com/iPaulPro/aFileChooser

Utilice la String path = FileUtils.getPath(context, uri) para convertir el Uri devuelto en una cadena de ruta utilizable en todas las versiones del SO. Vea más sobre esto aquí: https://.com/a/20559175/860488


Esto no requiere permisos especiales, y funciona con Storage Access Framework, así como con el patrón ContentProvider no oficial (ruta del archivo en el campo _data ).

/** * Get a file path from a Uri. This will get the the path for Storage Access * Framework Documents, as well as the _data field for the MediaStore and * other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @author paulburke */ public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); }

Vea una versión actualizada de este método here .


He combinado varias respuestas en una solución de trabajo que resulta con la ruta del archivo

El tipo Mime es irrelevante para el propósito de ejemplo.

Intent intent; if(Build.VERSION.SDK_INT >= 19){ intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); }else{ intent = new Intent(Intent.ACTION_GET_CONTENT); } intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setType("application/octet-stream"); if(isAdded()){ startActivityForResult(intent, RESULT_CODE); }

Resultado de manejo

@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) { Uri uri = data.getData(); if (uri != null && !uri.toString().isEmpty()) { if(Build.VERSION.SDK_INT >= 19){ final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; //noinspection ResourceType getActivity().getContentResolver() .takePersistableUriPermission(uri, takeFlags); } String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri); // do what you need with it... } } }

FilePickUtils

import android.annotation.SuppressLint; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; public class FilePickUtils { private static String getPathDeprecated(Context ctx, Uri uri) { if( uri == null ) { return null; } String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null); if( cursor != null ){ int column_index = cursor .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } return uri.getPath(); } public static String getSmartFilePath(Context ctx, Uri uri){ if (Build.VERSION.SDK_INT < 19) { return getPathDeprecated(ctx, uri); } return FilePickUtils.getPath(ctx, uri); } @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } }


Para aquellos que aún utilizan el código de @Paul Burke con Android SDK versión 23 y superior, si su proyecto cumplió con el error, indica que falta EXTERNAL_PERMISSION y está seguro de que ya ha agregado el permiso de usuario en su archivo AndroidManifest.xml. Esto se debe a que es posible que en la API de Android 23 o superior y Google lo haga necesario para garantizar el permiso nuevamente mientras realiza la acción para acceder al archivo en tiempo de ejecución.

Eso significa que: si su versión de SDK es 23 o superior, se le pedirá permiso de LEER Y ESCRIBIR mientras selecciona el archivo de imagen y desea conocer su URI.

Y a continuación está mi código, además de la solución de Paul Burke. Agrego estos códigos y mi proyecto comienza a funcionar bien.

private static final int REQUEST_EXTERNAL_STORAGE = 1; private static final String[] PERMISSINOS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; public static void verifyStoragePermissions(Activity activity) { int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( activity, PERMISSINOS_STORAGE, REQUEST_EXTERNAL_STORAGE ); } }

Y en tu actividad y fragmento donde solicitas el URI:

private void pickPhotoFromGallery() { CompatUtils.verifyStoragePermissions(this); Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY); startActivityForResult(Intent.createChooser(intent, "选择照片"), REQUEST_PHOTO_LIBRARY); }

En mi caso, CompatUtils.java es donde defino el método verifyStoragePermissions (como tipo estático para poder llamarlo dentro de otra actividad).

También debería tener más sentido si primero se establece un estado if para ver si la versión actual del SDK es superior a 23 antes de llamar al método verifyStoragePermissions.


Prueba esto:

if (Build.VERSION.SDK_INT <19){ Intent intent = new Intent(); intent.setType("image/jpeg"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED); } else { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/jpeg"); startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != Activity.RESULT_OK) return; if (null == data) return; Uri originalUri = null; if (requestCode == GALLERY_INTENT_CALLED) { originalUri = data.getData(); } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) { originalUri = data.getData(); final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // Check for the freshest data. getContentResolver().takePersistableUriPermission(originalUri, takeFlags); } loadSomeStreamAsynkTask(originalUri); }

Probablemente necesita

@SuppressLint ("NewApi")

para

takePersistableUriPermission


Solo quería decir que esta respuesta es brillante y la estoy usando durante mucho tiempo sin problemas. Pero hace algún tiempo, me topé con un problema que el content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf descargas devuelve URI en el content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf formato content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf y por lo tanto la aplicación se NumberFormatException con la NumberFormatException ya que es imposible analizar sus segmentos uri durante tanto tiempo. Pero raw: segment contiene uri directo que puede usarse para recuperar un archivo referenciado. Así que lo arreglé reemplazando isDownloadsDocument(uri) if contento con lo siguiente:

final String id = DocumentsContract.getDocumentId(uri); if (!TextUtils.isEmpty(id)) { if (id.startsWith("raw:")) { return id.replaceFirst("raw:", ""); } try { final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } catch (NumberFormatException e) { Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e); return null; } }


Tal como lo mencionó Commonsware, no debe asumir que la transmisión que obtiene a través de ContentResolver se puede convertir en un archivo.

Lo que realmente debería hacer es abrir el InputStream desde el ContentProvider , luego crear un Bitmap fuera de él. Y funciona en 4.4 y versiones anteriores también, sin necesidad de reflexión.

//cxt -> current context InputStream input; Bitmap bmp; try { input = cxt.getContentResolver().openInputStream(fileUri); bmp = BitmapFactory.decodeStream(input); } catch (FileNotFoundException e1) { }

Por supuesto, si maneja imágenes grandes, debe cargarlas con inSampleSize apropiado: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Pero ese es otro tema.


Tuvo el mismo problema, probé la solución anterior pero, aunque funcionó en general, por alguna razón recibí la denegación del permiso en el proveedor de contenido Uri para algunas imágenes, aunque tenía el permiso android.permission.MANAGE_DOCUMENTS agregado correctamente.

De todos modos, encontré otra solución que es forzar la apertura de la galería de imágenes en lugar de la vista de documentos KITKAT con:

// KITKAT i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

y luego cargar la imagen:

Uri selectedImageURI = data.getData(); input = c.getContentResolver().openInputStream(selectedImageURI); BitmapFactory.decodeStream(input , null, opts);

EDITAR

ACTION_OPEN_DOCUMENT posible que ACTION_OPEN_DOCUMENT requiera que se ACTION_OPEN_DOCUMENT permisos, etc. y, generalmente, a menudo se producen excepciones de seguridad ...

Otra solución es utilizar el ACTION_GET_CONTENT combinado con c.getContentResolver().openInputStream(selectedImageURI) que funcionará tanto en pre-KK como en KK. Kitkat usará la vista de documentos nuevos y esta solución funcionará con todas las aplicaciones como Fotos, Galería, Explorador de archivos, Dropbox, Google Drive, etc.) pero recuerde que cuando use esta solución debe crear una imagen en su onActivityResult() y guárdelo en la tarjeta SD, por ejemplo. Volver a crear esta imagen a partir del uri guardado en el próximo lanzamiento de la aplicación generaría una excepción de seguridad en la resolución de contenido, incluso cuando agregue marcas de permiso como se describe en los documentos de la API de Google (eso es lo que sucedió cuando hice algunas pruebas)

Además, las pautas de la API para desarrolladores de Android sugieren:

ACTION_OPEN_DOCUMENT no pretende ser un reemplazo para ACTION_GET_CONTENT. El que debes usar depende de las necesidades de tu aplicación:

Use ACTION_GET_CONTENT si desea que su aplicación simplemente lea / importe datos. Con este enfoque, la aplicación importa una copia de los datos, como un archivo de imagen.

Use ACTION_OPEN_DOCUMENT si desea que su aplicación tenga acceso persistente a largo plazo a los documentos que son propiedad de un proveedor de documentos. Un ejemplo sería una aplicación de edición de fotos que permite a los usuarios editar imágenes almacenadas en un proveedor de documentos.


Pregunta

Cómo obtener una ruta de archivo real desde un URI

Responder

A mi entender, no necesitamos obtener la ruta del archivo desde un URI porque en la mayoría de los casos podemos usar el URI directamente para realizar nuestro trabajo (como 1. obtener bitmap 2. enviar un archivo al servidor, etc.) .)

1. Enviando al servidor.

Podemos enviar directamente el archivo al servidor usando solo el URI.

Usando el URI podemos obtener InputStream, que podemos enviar directamente al servidor usando MultiPartEntity.

Ejemplo

/** * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application). * * @param uri URI. * @param context Context. * @return Multi Part Entity. */ public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) { MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8")); try { InputStream inputStream = mContext.getContentResolver().openInputStream(uri); if (inputStream != null) { ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context)); multipartEntity.addPart("[YOUR_KEY]", contentBody); } } catch (Exception exp) { Log.e("TAG", exp.getMessage()); } return multipartEntity; } /** * Used to get a file name from a URI. * * @param uri URI. * @param context Context. * @return File name from URI. */ public String getFileNameFromUri(final Uri uri, final Context context) { String fileName = null; if (uri != null) { // Get file name. // File Scheme. if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { File file = new File(uri.getPath()); fileName = file.getName(); } // Content Scheme. else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null); if (returnCursor != null && returnCursor.moveToFirst()) { int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); fileName = returnCursor.getString(nameIndex); returnCursor.close(); } } } return fileName; }

2. Obtener un mapa de bits de un URI

Si el URI está apuntando a la imagen, obtendremos un mapa de bits, de lo contrario null:

/** * Used to create bitmap for the given URI. * <p> * 1. Convert the given URI to bitmap. * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap. * 3. Create bitmap bitmap depending on the ration from URI. * 4. Reference - http://.com/questions/3879992/how-to-get-bitmap-from-an-uri * * @param context Context. * @param uri URI to the file. * @param bitmapSize Bitmap size required in PX. * @return Bitmap bitmap created for the given URI. * @throws IOException */ public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException { // 1. Convert the given URI to bitmap. InputStream input = context.getContentResolver().openInputStream(uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); onlyBoundsOptions.inJustDecodeBounds = true; onlyBoundsOptions.inDither = true;//optional onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional BitmapFactory.decodeStream(input, null, onlyBoundsOptions); input.close(); if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { return null; } // 2. Calculate ratio. int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0; // 3. Create bitmap. BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); bitmapOptions.inDither = true;//optional bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return bitmap; } /** * For Bitmap option inSampleSize - We need to give value in power of two. * * @param ratio Ratio to be rounded of to power of two. * @return Ratio rounded of to nearest power of two. */ private static int getPowerOfTwoForSampleRatio(final double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); if (k == 0) return 1; else return k; }

Comentarios

  1. Android no proporciona ningún método para obtener la ruta del archivo desde una URI, y en la mayoría de las respuestas anteriores hemos codificado algunas constantes, lo que puede interrumpir el lanzamiento de la función (lo siento, puedo estar equivocado).
  2. Antes de ir directamente a una solución para obtener la ruta del archivo desde un URI, intente si puede resolver su caso de uso con un URI y los métodos predeterminados de Android.

Referencia

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html

Este es un hack total, pero esto es lo que hice ...

Así que mientras jugaba con la configuración de DocumentsProvider , noté que el código de muestra (en getDocIdForFile, alrededor de la línea 450) genera una identificación única para un documento seleccionado basado en la ruta (única) del archivo en relación a la raíz especificada que le das (es decir, lo que pones mBaseDiren la línea 96).

Entonces el URI termina buscando algo como:

content://com.example.provider/document/root:path/to/the/file

Como dicen los documentos, está asumiendo que solo hay una raíz (en mi caso, Environment.getExternalStorageDirectory()pero puede usarla en otra parte ... luego toma la ruta del archivo, comenzando en la raíz, y la convierte en la ID única, antes de " root:". Así que puede determinar la ruta eliminando la "/document/root:"parte de uri.getPath (), creando una ruta de archivo real haciendo algo como esto:

public void onActivityResult(int requestCode, int resultCode, Intent data) { // check resultcodes and such, then... uri = data.getData(); if (uri.getAuthority().equals("com.example.provider")) { String path = Environment.getExternalStorageDirectory(0.toString() .concat("/") .concat(uri.getPath().substring("/document/root:".length()))); doSomethingWithThePath(path); } else { // another provider (maybe a cloud-based service such as GDrive) // created this uri. So handle it, or don''t. You can allow specific // local filesystem providers, filter non-filesystem path results, etc. }

Lo sé.Es vergonzoso, pero funcionó. Nuevamente, esto depende de que use su propio proveedor de documentos en su aplicación para generar la identificación del documento.

(Además, hay una mejor manera de construir la ruta de acceso que no asuma que "/" es el separador de ruta, etc. Pero entiendes la idea).


Esto funcionó bien para mi:

else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK) { Uri uri = data.getData(); Log.i(TAG, "old uri = " + uri); dumpImageMetaData(uri); try { ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Log.i(TAG, "File descriptor " + fileDescriptor.toString()); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); options.inSampleSize = BitmapHelper.calculateInSampleSize(options, User.PICTURE_MAX_WIDTH_IN_PIXELS, User.PICTURE_MAX_HEIGHT_IN_PIXELS); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); imageViewPic.setImageBitmap(bitmap); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); // get byte array here byte[] picData = stream.toByteArray(); ParseFile picFile = new ParseFile(picData); user.setProfilePicture(picFile); } catch(FileNotFoundException exc) { Log.i(TAG, "File not found: " + exc.toString()); } }


He intentado varias de las respuestas aquí, y creo que tengo una solución que funcionará cada vez y también gestionará los permisos.

Se basa en la solución inteligente de LEO. Esta publicación debe contener todo el código que necesita para hacer que esto funcione, y debería funcionar en cualquier versión de teléfono y Android;)

Para poder elegir un archivo de una tarjeta SD, necesitará esto en su manifiesto:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Constantes:

private static final int PICK_IMAGE = 456; // Whatever number you like public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like

Verifique el permiso y lanceImagePick si es posible

if (ContextCompat.checkSelfPermission(getThis(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getThis(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL); } else { launchImagePick(); }

Respuesta de permiso

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) { launchImagePick(); } }

Administrar la respuesta de permiso

public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) { if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission was granted, yay! Do the // contacts-related task you need to do. return true; } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) { boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_EXTERNAL_STORAGE); if (!showRationale) { // The user also CHECKED "never ask again". // You can either enable some fall back, // disable features of your app // or open another dialog explaining // again the permission and directing to // the app setting. } else { // The user did NOT check "never ask again". // This is a good place to explain the user // why you need the permission and ask if he/she wants // to accept it (the rationale). } } else { // Permission denied, boo! Disable the // functionality that depends on this permission. } } return false; }

Lanzar selección de imagen

private void launchImagePick() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, PICK_IMAGE); // see onActivityResult }

Administrar la respuesta de selección de imagen

@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE) { if (resultCode == Activity.RESULT_OK) { if (data != null && data.getData() != null) { try { InputStream inputStream = getContentResolver().openInputStream(data.getData()) if (inputStream != null) { // No special persmission needed to store the file like that FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE); final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer)) > -1) { fos.write(buffer, 0, bytesRead); } inputStream.close(); fos.close(); File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME); // Do whatever you want with the File // Delete when not needed anymore deleteFile(FILE_TEMP_NAME); } } catch (Exception e) { e.printStackTrace(); } } else { // Error display } } else { // The user did not select any image } } }

Eso es todo amigos; esto funciona para mí en todos los teléfonos que tengo.


Gracias a la respuesta de Paul Burke, tuve muchos problemas para resolver la ruta URI de la tarjeta SD externa, ya que la mayoría de las funciones "incorporadas" sugeridas devuelven rutas que no se resuelven en archivos.

Sin embargo, este es mi enfoque de su // TODO manejar volúmenes no primarios .

String resolvedPath = ""; File[] possibleExtSdComposites = context.getExternalFilesDirs(null); for (File f : possibleExtSdComposites) { // Reset final path resolvedPath = ""; // Construct list of folders ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/"))); // Look for folder "<your_application_id>" int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID); // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard''s root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2)); // Construct list containing full possible path to the file hierarchyList.add(tail); String possibleFilePath = TextUtils.join("/", hierarchyList); // If file is found --> success if (idx != -1 && new File(possibleFilePath).exists()) { resolvedPath = possibleFilePath; break; } } if (!resolvedPath.equals("")) { return resolvedPath; } else { return null; }

Tenga en cuenta que depende de la jerarquía que podría ser diferente en cada fabricante de teléfonos: no los he probado todos (hasta ahora funcionó bien en Xperia Z3 API 23 y Samsung Galaxy A3 API 23).

Por favor, confirme si no funciona bien en otros lugares.


Intente evitar el uso del método takePersistableUriPermission porque generó una excepción de tiempo de ejecución para mí. / ** * Seleccionar de la galería. * /

public void selectFromGallery() { if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED); } else { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED); } }

OnActivity por resultado para manejar los datos de la imagen:

@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {

//gallery intent result handling before kit-kat version if(requestCode==AppConstants.GALLERY_INTENT_CALLED && resultCode == RESULT_OK) { Uri selectedImage = data.getData(); String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String filePath = cursor.getString(columnIndex); cursor.close(); photoFile = new File(filePath); mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP); } //gallery intent result handling after kit-kat version else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED && resultCode == RESULT_OK) { Uri selectedImage = data.getData(); InputStream input = null; OutputStream output = null; try { //converting the input stream into file to crop the //selected image from sd-card. input = getApplicationContext().getContentResolver().openInputStream(selectedImage); try { photoFile = mImgCropping.createImageFile(); } catch (IOException e) { e.printStackTrace(); }catch(Exception e) { e.printStackTrace(); } output = new FileOutputStream(photoFile); int read = 0; byte[] bytes = new byte[1024]; while ((read = input.read(bytes)) != -1) { try { output.write(bytes, 0, read); } catch (IOException e) { e.printStackTrace(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }


La respuesta a tu pregunta es que necesitas tener permisos. Escriba el siguiente código en su archivo manifest.xml:

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission> <uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`

Funcionó para mí ...


La respuesta de @paul burke funciona bien tanto para la cámara como para las imágenes de la galería para niveles de API 19 y superiores, pero no funciona si el SDK mínimo de su proyecto de Android está configurado por debajo de 19, y algunas de las respuestas anteriores no funcionan tanto para la galería como cámara. Bueno, he modificado el código de @paul burke que funciona para un nivel de API inferior a 19. A continuación se muestra el código.

public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; Log.i("URI",uri+""); String result = uri+""; // DocumentProvider // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { if (isKitKat && (result.contains("media.documents"))) { String[] ary = result.split("/"); int length = ary.length; String imgary = ary[length-1]; final String[] dat = imgary.split("%3A"); final String docId = dat[1]; final String type = dat[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { } else if ("audio".equals(type)) { } final String selection = "_id=?"; final String[] selectionArgs = new String[] { dat[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; }


Si alguien está interesado, hice una versión funcional de Kotlin para ACTION_GET_CONTENT:

var path: String = uri.path // uri = any content Uri val databaseUri: Uri val selection: String? val selectionArgs: Array<String>? if (path.contains("/document/image:")) { // files selected from "Documents" databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI selection = "_id=?" selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1]) } else { // files selected from all other sources, especially on Samsung devices databaseUri = uri selection = null selectionArgs = null } try { val projection = arrayOf(MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.DATE_TAKEN) // some example data you can query val cursor = context.contentResolver.query(databaseUri, projection, selection, selectionArgs, null) if (cursor.moveToFirst()) { // do whatever you like with the data } cursor.close() } catch (e: Exception) { Log.e(TAG, e.message, e) }