unresolved that support studio geturiforfile fromfile fileprovider failed content configured cache authorities android android-contentprovider android-support-library android-fileprovider

android - that - ¿Cómo se usa FileProvider para compartir contenido con otras aplicaciones?



internal cache content provider (8)

Estoy buscando una forma de compartir (no ABRIR) correctamente un archivo interno con una aplicación externa utilizando el FileProvider la biblioteca de soporte de FileProvider .

Siguiendo el ejemplo en los documentos,

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.android.supportv4.my_files" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/my_paths" /> </provider>

y el uso de ShareCompat para compartir un archivo a otras aplicaciones de la siguiente manera:

ShareCompat.IntentBuilder.from(activity) .setStream(uri) // uri from FileProvider .setType("text/html") .getIntent() .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

no funciona, ya que FLAG_GRANT_READ_URI_PERMISSION solo concede permiso para el URI especificado en los data de la intención, no el valor de EXTRA_STREAM extra (como fue establecido por setStream ).

Traté de poner en peligro la seguridad al configurar android:exported FileProvider a true para el proveedor, pero FileProvider verifica internamente si se exporta, cuando es así, arroja una excepción.


Al utilizar FileProvider desde la biblioteca de soporte, debe otorgar y revocar permisos manualmente (en tiempo de ejecución) para que otras aplicaciones lean Uri específico. Utilice los métodos Context.grantUriPermission y Context.revokeUriPermission .

Por ejemplo:

//grant permision for app with package "packegeName", eg. before starting other app via intent context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); //revoke permisions context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

Como último recurso, si no puede proporcionar el nombre del paquete, puede otorgar el permiso a todas las aplicaciones que puedan manejar una intención específica:

//grant permisions for all apps that can handle given intent Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); ... List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); }

Método alternativo según la documentation :

  • Coloque el URI de contenido en un Intento llamando a setData ().
  • Luego, llame al método Intent.setFlags () con FLAG_GRANT_READ_URI_PERMISSION o FLAG_GRANT_WRITE_URI_PERMISSION o con ambos.
  • Finalmente, envíe el Intento a otra aplicación. La mayoría de las veces, haces esto llamando a setResult ().

    Los permisos otorgados en un intento permanecen vigentes mientras la pila de la actividad receptora está activa. Cuando la pila termina, el
    los permisos se eliminan automáticamente Permisos otorgados a uno
    La actividad en una aplicación de cliente se extiende automáticamente a otra
    componentes de esa aplicación.

Por cierto. si es necesario, puede copiar el origen de FileProvider y cambiar el método attachInfo para evitar que el proveedor attachInfo si se exporta.


Código de trabajo completo muestra cómo compartir el archivo desde la carpeta de la aplicación interna. Probado en Android 7 y Android 5.

AndroidManifest.xml

</application> .... <provider android:name="android.support.v4.content.FileProvider" android:authorities="android.getqardio.com.gmslocationtest" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> </application>

xml / provider_paths

<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="share" path="external_files"/> </paths>

Código en sí

File imagePath = new File(getFilesDir(), "external_files"); imagePath.mkdir(); File imageFile = new File(imagePath.getPath(), "test.jpg"); // Write data in your file Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile); Intent intent = ShareCompat.IntentBuilder.from(this) .setStream(uri) // uri from FileProvider .setType("text/html") .getIntent() .setAction(Intent.ACTION_VIEW) //Change if needed .setDataAndType(uri, "image/*") .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent);


Dado que, como dice Phil en su comentario sobre la pregunta original, esto es único y no hay otra información sobre SO on en google, pensé que también debería compartir mis resultados:

En mi aplicación, FileProvider resolvió compartir archivos usando la intención de compartir. No fue necesaria una configuración o código especial, más allá de eso para configurar FileProvider. En mi manifest.xml coloqué:

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.my.apps.package.files" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/my_paths" /> </provider>

En my_paths.xml tengo:

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

En mi código tengo:

Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setType("application/xml"); Uri uri = FileProvider.getUriForFile(this, "com.my.apps.package.files", fileToShare); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_file)));

Y puedo compartir mi tienda de archivos en mi almacenamiento privado de aplicaciones con aplicaciones como Gmail y Google Drive sin ningún problema.


En mi aplicación, FileProvider funciona perfectamente y puedo adjuntar archivos internos almacenados en el directorio de archivos a clientes de correo electrónico como Gmail, Yahoo, etc.

En mi manifiesto como se menciona en la documentación de Android coloqué:

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.package.name.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>

Y como mis archivos estaban almacenados en el directorio de archivos raíz, los filepaths.xml fueron los siguientes:

<paths> <files-path path="." name="name" />

Ahora en el código:

File file=new File(context.getFilesDir(),"test.txt"); Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Test"); shareIntent.setType("text/plain"); shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {"email-address you want to send the file to"}); Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider", file); ArrayList<Uri> uris = new ArrayList<Uri>(); uris.add(uri); shareIntent .putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); try { context.startActivity(Intent.createChooser(shareIntent , "Email:").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch(ActivityNotFoundException e) { Toast.makeText(context, "Sorry No email Application was found", Toast.LENGTH_SHORT).show(); } }

Esto funcionó para mí. Espero que esto ayude :)


Esta solución funciona para mí desde OS 4.4. Para que funcione en todos los dispositivos, agregué una solución alternativa para los dispositivos más antiguos. Esto asegura que siempre se use la solución más segura.

Manifest.xml:

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.package.name.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>

file_paths.xml:

<paths> <files-path name="app_directory" path="directory/"/> </paths>

Java:

public static void sendFile(Context context) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); String dirpath = context.getFilesDir() + File.separator + "directory"; File file = new File(dirpath + File.separator + "file.txt"); Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file); intent.putExtra(Intent.EXTRA_STREAM, uri); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Workaround for Android bug. // grantUriPermission also needed for KITKAT, // see https://code.google.com/p/android/issues/detail?id=76683 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(packageName, attachmentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } } if (intent.resolveActivity(context.getPackageManager()) != null) { context.startActivity(intent); } } public static void revokeFileReadPermission(Context context) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { String dirpath = context.getFilesDir() + File.separator + "directory"; File file = new File(dirpath + File.separator + "file.txt"); Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file); context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } }

El permiso se revocó con revokeFileReadPermission () en los métodos onResume y onDestroy () del Fragment o la Activity.


Por lo que puedo decir, esto solo funcionará en las versiones más nuevas de Android, por lo que probablemente tengas que encontrar una forma diferente de hacerlo. Esta solución funciona para mí en 4.4, pero no en 4.0 o 2.3.3, por lo que esta no será una forma útil de compartir contenido para una aplicación que se debe ejecutar en cualquier dispositivo Android.

En manifest.xml:

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.myapp.SharingActivity" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>

Tome nota cuidadosamente de cómo especifica las autoridades. Debe especificar la actividad desde la cual creará el URI e iniciará el intento de compartir, en este caso la actividad se llama SharingActivity. ¡Este requisito no es obvio en los documentos de Google!

file_paths.xml:

<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="just_a_name" path=""/> </paths>

Tenga cuidado de cómo especifica la ruta. Lo anterior está predeterminado en la raíz de su almacenamiento interno privado.

En SharingActivity.java:

Uri contentUri = FileProvider.getUriForFile(getActivity(), "com.mydomain.myapp.SharingActivity", myFile); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setType("image/jpeg"); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(Intent.createChooser(shareIntent, "Share with"));

En este ejemplo, estamos compartiendo una imagen JPEG.

Finalmente, probablemente sea una buena idea asegurarse de que ha guardado el archivo correctamente y de que puede acceder a él con algo como esto:

File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg"); if(myFile != null){ Log.d(TAG, "File found, file description: "+myFile.toString()); }else{ Log.w(TAG, "File not found!"); }


Si obtienes una imagen de la cámara, ninguna de estas soluciones funciona para Android 4.4. En este caso, es mejor verificar las versiones.

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (intent.resolveActivity(getContext().getPackageManager()) != null) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { uri = Uri.fromFile(file); } else { uri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".provider", file); } intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, CAMERA_REQUEST); }


solo para mejorar la respuesta dada anteriormente: si obtiene NullPointerEx:

también puedes usar getApplicationContext () sin contexto

List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); }