FileProvider

251 views
Skip to first unread message

Jaume Tugores

unread,
Sep 22, 2016, 12:32:58 AM9/22/16
to desarrolladores-android
Acabo de actualizar mi movil a Android N y pruebo mi aplicación y primer error: android error exposed beyond app through Intent.getData(), resulta que ahora el acceso a ficheros por aplicaciones externas va más protegido y hay que hacerlo a traves de un file provider que hay que declarar antes, he visto algo de informacion, en ingles, pero hago pruebas y no consigo hacerlo funcionar.
Mi aplicación lo que hace es descargar un pdf que luego tengo que abrir.
Hago una petición aver si alguien que tenga claro este nuevo modo de funcionamiento, puede explicarlo para que lo entendamos los mas inexpertos(yo por ejemplo).
Gracias.

Eduardo Martin Cabrera

unread,
Sep 22, 2016, 11:38:51 AM9/22/16
to desarrollad...@googlegroups.com
Tener en cuenta que hay diferencia entre acceder al filesystem o al sd card, si se trata de este ultimo caso hay que agregar la/s ruta/s


En manifest.xml agregar un provider (FileProvider)
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
       <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
</provider>

...y en en la carpeta xml (res/xml), agregar un xml con los paths permitidos para acceder ...
..o bien la ruta raíz como en el sig. ejemplo:

<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
... luego obtener el Uri para el fichero y opcionalmente obtener permiso temporal
(ver seccion "Granting Temporary Permissions to a URI" en https://developer.android.com/reference/android/support/v4/content/FileProvider.htmlyo claro )
espero que la info te sirva, saludos!



--
Para participar es necesario que leas detenidamente las normas del grupo: https://goo.gl/xeTRQm
---
Has recibido este mensaje porque estás suscrito al grupo "desarrolladores-android" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a desarrolladores-android+unsub...@googlegroups.com.
Para publicar en este grupo, envía un correo electrónico a desarrolladores-android@googlegroups.com.
Visita este grupo en https://groups.google.com/group/desarrolladores-android.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/desarrolladores-android/c98a594d-ab06-4134-9329-0676c77a2206%40googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

Jaime Tugores

unread,
Sep 22, 2016, 2:28:35 PM9/22/16
to desarrollad...@googlegroups.com
Gracias por la info, voy a probar a ver si lo consigo.
Has recibido este mensaje porque estás suscrito a un tema del grupo "desarrolladores-android" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/desarrolladores-android/LluIkw4lO8s/unsubscribe.
Para anular la suscripción a este grupo y a todos sus temas, envía un correo electrónico a desarrolladores-a...@googlegroups.com.
Para publicar en este grupo, envía un correo electrónico a desarrollad...@googlegroups.com.

Para acceder a más opciones, visita https://groups.google.com/d/optout.


--
______________Saludos Jaume._________________

Jaume Tugores

unread,
Sep 24, 2016, 3:52:09 AM9/24/16
to desarrolladores-android
Estos temas ya los había visto y no consigo solucionar el problema.
El problema esta al crear un intent para ver un pdf que he bajado anteriormente y que se ha escrito correctamente en la carpeta: sdcard\micarpeta\archivo.pdf, lo puedo verificar porque con el explorador lo veo y puedo abrir el archivo pdf y verlo correctamente, pero cuando intento abrirlo desde mi aplicacion da error, mi codigo para esto es el siguiente:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(miPdf),"application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent); --> aqui da el error

Gracias.

Martin

unread,
Sep 24, 2016, 12:56:17 PM9/24/16
to desarrolladores-android
Hola, pega el logcat

Jaume Tugores

unread,
Sep 24, 2016, 1:00:47 PM9/24/16
to desarrolladores-android
D/pdf: JATUMA0000_trab_A161023001.pdf
D/pdf: existe
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: es.puigverd.puigverd, PID: 28411
                  android.os.FileUriExposedException: file:///storage/emulated/0/pverd/JATUMA0000_trab_A161023001.pdf exposed beyond app through Intent.getData()
                      at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
                      at android.net.Uri.checkFileUriExposed(Uri.java:2346)
                      at android.content.Intent.prepareToLeaveProcess(Intent.java:8933)
                      at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
                      at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
                      at android.app.Activity.startActivityForResult(Activity.java:4224)
                      at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:48)
                      at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:75)
                      at android.app.Activity.startActivityForResult(Activity.java:4183)
                      at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:871)
                      at android.app.Activity.startActivity(Activity.java:4507)
                      at android.app.Activity.startActivity(Activity.java:4475)
                      at es.puigverd.puigverd.otss.otss.verpdf(otss.java:592)
                      at es.puigverd.puigverd.otss.otss.InfTrabajo(otss.java:580)
                      at es.puigverd.puigverd.otss.otss.onClick(otss.java:291)
                      at android.view.View.performClick(View.java:5609)
                      at android.view.View$PerformClick.run(View.java:22259)
                      at android.os.Handler.handleCallback(Handler.java:751)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6077)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Martin

unread,
Sep 24, 2016, 5:25:28 PM9/24/16
to desarrolladores-android
Parece que no estas usando el FileProvider, si estas apuntando a usar android N hay que usar FileProvider.getUriForFile en vez de Uri.fromFile para obtener la ruta del archivo, si usas el segundo (es el clásico pero que ya no va para android N, el Uri.fromFile) se expone el scheme file:/// tal como se ve en el logcat y te lanza esa exception; proba lo siguiente:

Agregar el provider (FileProvider) al manifest y el archivo con la/s ruta/s (provider_paths)
En el código cambiar el uso de Uri.fromFile por FileProvider.getUriForFile

y no estará demás revisar (usando Log.d por ejemplo) que la ruta a usar, es decir la del pdf no contenga el scheme file///

Jaume Tugores

unread,
Sep 25, 2016, 2:04:37 AM9/25/16
to desarrolladores-android
Esto del fileprovider, no me cuadra, por lo que estoy viendo el fileprovider se usa para pasar un archivo que está en el directorio privado de la aplicación a otra aplicación externa, como podria ser el caso de tener que abrir un pdf desde el directorio privado de mi aplicación, pero este no es el caso, yo he sacado este pdf y lo he puesto fuera de las carpetas privadas, está concretamente en: /storage/emulated/0/pverd/trabA161023001.pdf    y desde el explorador de archivos lo veo correctamente y lo puedo abrir.
Podeis decirme si esto es correcto o si tambien para este caso se tiene que usar el fileprovider y en este caso como se declara el path para estos archivos publicos.

Ahora mismo el fileprovider que tendo declarado es este:
..res\xml\provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="pdfss" path="pverd/"/>
</paths>

y en el manifiest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>


ahora mismo la llamada la hago asi:(aunque se que esta mal, pero no se como montarla)
String carpeta = Environment.getExternalStorageDirectory() + File.separator + "pverd";
File fCarpeta = new File(carpeta);
File miPdf = new File(carpeta, pfpdf);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileProvider.getUriForFile(this, "es.puigverd.puigverd.fileprovider", miPdf),"application/pdf");   //-mipdf tiene este valor: "/storage/emulated/0/pverd/trabA161023001.pdf"
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);


el logcat:
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: es.puigverd.puigverd, PID: 15253
                  java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
                      at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:561)
                      at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:535)
                      at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:377)
                      at es.puigverd.puigverd.otss.otss.verpdf(otss.java:590)
                      at es.puigverd.puigverd.otss.otss.InfTrabajo(otss.java:580)
                      at es.puigverd.puigverd.otss.otss.onClick(otss.java:291)
                      at android.view.View.performClick(View.java:5609)
                      at android.view.View$PerformClick.run(View.java:22259)
                      at android.os.Handler.handleCallback(Handler.java:751)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6077)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Martin

unread,
Sep 25, 2016, 3:17:24 PM9/25/16
to desarrolladores-android
Buenas, en los códigos que mostrás creo que para tu caso (acceder al SD CARD) debe ser
<external-path name="external_files" path="pverd" />
en lugar del <file-path
...

ademas veo una diferencia, que no es el mismo autority en el atributo del provider que el del código java
,en el manifest usas:
  android:authorities="${applicationId}.provider"
 
que debe producir algo como:
es.puigverd.puigverd.provider

y en código estas usando:
  FileProvider.getUriForFile(this, "es.puigverd.puigverd.fileprovider ...

y creo que debe ser
  FileProvider.getUriForFile(this, "es.puigverd.puigverd.provider
 

Jaume Tugores

unread,
Sep 26, 2016, 12:39:35 AM9/26/16
to desarrolladores-android
Gracias Martín, tenías razón, entre los cambios que me has aconsejado y pruebas que he ido haciendo ha dado resultado.
Luego he tenido un pequeño problema y es que la  pantalla me quedaba en negro con el nombre del archivo pero buscando he visto que era cuestión de darle permisos,
Al final el código que me ha funcionado ha quedado asi:

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

y en el manifiest:
<provider
android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>

y la llamada:
String carpeta = Environment.getExternalStorageDirectory() + File.separator + "pverd";
File miPdf = new File(carpeta, pfpdf);
Uri pdfURI = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider", miPdf);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(pdfURI,"application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

Aquí queda por si le puede servir a alguien
Lo doy por finalizado.
Gracias Martín.

Martin

unread,
Sep 26, 2016, 4:50:48 PM9/26/16
to desarrolladores-android
Que bueno que hayas solucionado el tema! Y la verdad es que los cambios que vinieron con android N deberían estar mas documentados, yo algunas veces mas allá de leer la doc ademas tengo que hacer varias pruebas para ver o deducir como se comportan algunas cosas y eso hace perder tiempo; en fin, saludos!
Reply all
Reply to author
Forward
0 new messages