La intención de compartir imágenes funciona para Gmail pero bloquea FB y twitter

Estoy tratando de permitir que el usuario comparta una imagen con otras aplicaciones en el dispositivo. La imagen se encuentra dentro de los archivos / subdirectorio del área de almacenamiento interno de mi aplicación. Funciona muy bien con Gmail, pero Facebook y Twitter se bloquean al responder a mi intención.

EDIT: Google+ también funciona bien.

Aquí están las secciones pertinentes del código.

En Application.xml

<provider android:name="android.support.v4.content.FileProvider" android:authorities="org.iforce2d.myapp.MyActivity" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> 

Xml / filepaths.xml

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

Este es el código compartido en mi actividad:

 File imagePath = new File(getContext().getFilesDir(), "shared"); File newFile = new File(imagePath, "snapshot.jpg"); Uri contentUri = FileProvider.getUriForFile(getContext(), "org.iforce2d.myapp.MyActivity", newFile); 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); List<ResolveInfo> resInfos = getPackageManager().queryIntentActivities(shareIntent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo info : resInfos) { getContext().grantUriPermission(info.activityInfo.packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } startActivity(Intent.createChooser(shareIntent, "Share image...")); 

El valor de contentUri cuando se registra es:

content://org.iforce2d.myapp.MyActivity/shared/snapshot.jpg

Sólo he comprobado esto con Gmail, Facebook y Twitter como las aplicaciones de recepción, pero los resultados han sido muy consistentes en una amplia gama de versiones de SO (de 2.2.1 a 4.4.3) y 7 dispositivos incluyen un Kindle.

Gmail funciona muy bien. La miniatura de la imagen aparece en la composición del correo y se adjunta correctamente al correo cuando se envía.

Twitter y Facebook se bloquean, como a continuación.

Aquí están los rastros de la pila de logcat que demuestran el problema que estas dos aplicaciones están teniendo, parece ser el mismo problema para ambos (esto se toma de 4.4.3, pero los errores eran prácticamente iguales toda la manera de nuevo a 2.2. 1, aunque con una redacción ligeramente diferente del mensaje de error):

 Caused by: java.lang.IllegalStateException: Couldn't read row 0, col 0 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it. at android.database.CursorWindow.nativeGetString(Native Method) at android.database.CursorWindow.getString(CursorWindow.java:434) at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51) at android.database.CursorWrapper.getString(CursorWrapper.java:114) at com.twitter.library.media.util.fa(Twttr:95) ... Caused by: java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it. at android.database.CursorWindow.nativeGetString(Native Method) at android.database.CursorWindow.getString(CursorWindow.java:434) at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51) at android.database.CursorWrapper.getString(CursorWrapper.java:114) at com.facebook.photos.base.media.MediaItemFactory.b(MediaItemFactory.java:233) ... 

Dado que compartir imágenes en Facebook y Twitter es algo que millones de personas hacen durante todo el día, estoy bastante sorprendido de que sea tan difícil de implementar: /

¿Puede alguien detectar algo que estoy haciendo mal aquí?

EDIT – La solución descrita en esta respuesta funciona, pero la solución proporcionada por Stefan es más elegante. Debe ser la respuesta aceptada.

Además, a partir de enero de 2015, la aplicación de Facebook ya no tiene este error (y ahora funciona con el FileProvider estándar), pero Twitter todavía lo hace. Esperemos que se vaya completamente lejos en el futuro, y ninguno de estos será necesario.


Mientras que el objeto FileProvider ve maravilloso en teoría, no parece funcionar cuando se comparte con muchas aplicaciones de terceros (mientras que los de Google, como G +, Gmail, & c funcionan perfectamente). O bien no expone información suficiente, o esas aplicaciones sólo asumen que está compartiendo archivos y, por lo tanto, se bloquea.

¡Es muy frustrante! : /

La única forma fiable que he encontrado para compartir archivos de imagen es copiarlos al directorio de almacenamiento externo (puede crear un subdirectorio .nomedia para evitar que aparezcan en la aplicación Galería).

Por ejemplo:

 Bitmap bitmapToShare = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); File pictureStorage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File noMedia = new File(pictureStorage, ".nomedia"); if (!noMedia.exists()) noMedia.mkdirs(); File file = new File(noMedia, "shared_image.png"); saveBitmapAsFile(bitmapToShare, file); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)); shareIntent.setType("image/png"); startActivity(shareIntent); 

Adición: por supuesto, también debe comprobar getExternalStorageState() antes de intentar escribir / copiar el archivo allí.

Twitter (erróneamente) asume que habrá una columna MediaStore.MediaColumns.DATA. Comenzando en KitKat el MediaStore devuelve null, así que por suerte, Twitter graciosamente maneja nulos, y hace lo correcto.

 public class FileProvider extends android.support.v4.content.FileProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor source = super.query(uri, projection, selection, selectionArgs, sortOrder); String[] columnNames = source.getColumnNames(); String[] newColumnNames = columnNamesWithData(columnNames); MatrixCursor cursor = new MatrixCursor(newColumnNames, source.getCount()); source.moveToPosition(-1); while (source.moveToNext()) { MatrixCursor.RowBuilder row = cursor.newRow(); for (int i = 0; i < columnNames.length; i++) { row.add(source.getString(i)); } } return cursor; } private String[] columnNamesWithData(String[] columnNames) { for (String columnName : columnNames) if (MediaStore.MediaColumns.DATA.equals(columnName)) return columnNames; String[] newColumnNames = Arrays.copyOf(columnNames, columnNames.length + 1); newColumnNames[columnNames.length] = MediaStore.MediaColumns.DATA; return newColumnNames; } } 
  • Cómo confirmar que Android Share Intent se ha completado correctamente
  • Cómo filtrar aplicaciones específicas para la intención ACTION_SEND (y establecer un texto diferente para cada aplicación)
  • Cambiar el nombre del archivo adjunto al compartir el archivo en el correo
  • Especificación de un filtro de intenciones SEND (compartido) para un servicio
  • La intención de compartir Google+ no puede acceder a la imagen
  • Compartir fotos con Android con FileProvider
  • Permisos de Android M al compartir datos con otras aplicaciones
  • Compartir en Facebook en android (como twitter)
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.