¿Cómo utilizar la compatibilidad con FileProvider para compartir contenido con otras aplicaciones?

11 minutos de lectura

Estoy buscando una manera de compartir correctamente (no ABRIR) un archivo interno con una aplicación externa usando la biblioteca de soporte de Android proveedor de archivos.

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 usar ShareCompat para compartir un archivo con 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 otorga permiso para el Uri especificado en el data de la intención, no del valor de la EXTRA_STREAM extra (como fue establecido por setStream).

Traté de comprometer la seguridad configurando android:exported para true para el proveedor, pero FileProvider comprueba internamente si se exporta, cuando es así, lanza una excepción.

  • +1 esta parece ser una pregunta verdaderamente original: hay nada en Google o Desbordamiento de pilapor lo que pude ver.

    – Phil

    23 de agosto de 2013 a las 14:31

  • Aquí hay una publicación para obtener una configuración básica de FileProvider utilizando un proyecto de ejemplo mínimo de github: stackoverflow.com/a/48103567/2162226 . Proporciona pasos sobre qué archivos copiar (sin realizar cambios) en un proyecto independiente local.

    – Gen Bo

    04/01/2018 a las 21:34

¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
Leszek

Utilizando FileProvider desde la biblioteca de soporte, debe otorgar y revocar manualmente los permisos (en tiempo de ejecución) para que otras aplicaciones lean Uri específicos. Utilizar Contexto.grantUriPermiso y Contexto.revokeUriPermission métodos.

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 pueden 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 el documentación:

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

    Los permisos otorgados en un Intent permanecen en vigor 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 cliente se extiende automáticamente a otras
    componentes de esa aplicación.

Por cierto. si lo necesitas puedes copiar fuente de FileProvider y cambio attachInfo método para evitar que el proveedor verifique si se exporta.

  • Utilizando el grantUriPermission método necesitamos proporcionar el nombre del paquete de la aplicación a la que queremos otorgar el permiso. Sin embargo, cuando se comparte, generalmente no sabemos qué aplicación es el destino para compartir.

    – Randy Sugianto ‘Yuku’

    20 de agosto de 2013 a las 14:08

  • ¿Puede proporcionar un código de trabajo para la última solución @Lecho?

    – StErMi

    31 de marzo de 2014 a las 7:45

  • ¿Hay un error de seguimiento abierto por el que el soporte FileProvider no funciona con setFlags, pero sí con grantUriPermission? ¿O esto no es un error, en cuyo caso, cómo no es esto un error?

    – RMN

    26 de septiembre de 2014 a las 0:28

  • Tenga en cuenta: si utiliza el enfoque con intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) tenga en cuenta que debe agregar los datos PRIMERO a la intención y solo luego agrega una bandera con permiso. De lo contrario, no funcionará.

    – Kirill Karmazin

    15 de octubre de 2018 a las 12:19


  • esto no funcionará ya que Android 11 cambia la visibilidad del paquete

    – ruif3r

    7 dic 2021 a las 22:45

¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
Diversos

Ejemplo de código completamente funcional sobre cómo compartir archivos desde la carpeta interna de la aplicación. Probado en Android 7 y Android 5.

AndroidManifest.xml

</application>
   ....
    <provider
        android:name="androidx.core.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);

  • el uso de la ShareCompat.IntentBuilder y el permiso de lectura de URI finalmente lo hizo por mí.

    – Cordón Rehn

    29/10/2016 a las 23:13

  • Descansos para otras versiones de Android

    –Oliver Dixon

    11 de mayo de 2017 a las 8:57

  • Esta es la única respuesta sobre el uso de la FileProvider eso funciona. Las otras respuestas se equivocan en el manejo de la intención (no usan ShareCompat.IntentBuilder), y esa intención no se puede abrir con aplicaciones externas de ninguna manera significativa. He pasado literalmente la mayor parte del día con estas tonterías hasta que finalmente encontré tu respuesta.

    – Jirafa Violeta

    18 de junio de 2019 a las 18:27


  • @VioletGiraffe Tampoco estoy usando ShareCompat pero el mío funciona. Mi problema era que estaba otorgando permisos de lectura a la intención de mi selector por error, cuando necesitaba otorgar permisos de lectura a mi intención ANTES de que se pasara a la intención del selector, así como al selector. Espero que tenga sentido. Solo asegúrese de otorgar permiso de lectura a la intención correcta.

    – Ryan

    30 sep 2019 a las 22:48

  • Probé ambos, usando ShareCompat y usando un Intent genérico. Ambos funcionaron en mi caso. Probado en API 23, 27, 29 y 30. Todo funcionó. Personalmente, me gusta la forma normal de Intent de compartir más, porque crea un buen selector de destino en las versiones más nuevas de Android. Mientras que al usar la capacidad ShareCompat, solo obtiene el selector de destino genérico y aburrido.

    – Akito

    21 de noviembre de 2021 a las 18:04


1646756890 482 ¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
Oliver Kranz

Esta solución me funciona 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 utilice la solución más segura.

Manifiesto.xml:

    <provider
        android:name="androidx.core.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>

rutas_archivo.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 revoca con revokeFileReadPermission() en los métodos onResume y onDestroy() del Fragmento o la Actividad.

  • Esta solución funcionó para mí, pero solo después de agregar intent.setDataAndType(uri, “video/*”); Por cierto, falta el archivo adjunto. La variable Uri es uri.

    – EdgarK

    21 de julio de 2016 a las 16:58


  • Quieres decir que file no cambiará de onResume para onDestroy?

    – CoolMind

    19 de febrero de 2018 a las 13:22

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

En mi aplicación, FileProvider funcionó de inmediato para compartir archivos usando la intención de compartir. No se necesitaba ninguna 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 almacén de archivos en el almacenamiento privado de mis aplicaciones con aplicaciones como Gmail y Google Drive sin ningún problema.

1646756890 146 ¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
Rasmusob

Por lo que puedo decir, esto solo funcionará en las versiones más nuevas de Android, por lo que probablemente tendrá 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 debe ejecutarse en cualquier dispositivo Android.

En manifiesto.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 cuidadosa de cómo especifica las autoridades. Debes especificar la actividad a partir de la cual crearás la URI y lanzarás la intención de compartir, en este caso la actividad se llama SharingActivity. ¡Este requisito no es obvio en los documentos de Google!

rutas_archivo.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 al especificar 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 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!");
}

1646756891 184 ¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
Adarsh ​​Chithrán

En mi aplicación, FileProvider funciona bien 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, las rutas de archivo.xml eran las 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 🙂

1646756892 360 ¿Como utilizar la compatibilidad con FileProvider para compartir contenido con
CoolMind

Si obtiene una imagen de la cámara, ninguna de estas soluciones funciona para Android 4.4. En este caso es mejor comprobar 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);
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad