Compartir fotos con Android con FileProvider

He leído toda la respuesta sobre este argumento pero siempre recibo un error de la aplicación que recibe mi foto.
La única manera que funcionó para mí, para toda la aplicación , fue esto (funciona porque los archivos de tarjetas SD son públicos para todas las aplicaciones ):

final File tmpFile = new File(context.getExternalCacheDir(), "exported.jpg"); Uri tmpFileUri = Uri.fromFile(tmpFile); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setDataAndType(tmpFileUri, "image/jpeg"); shareIntent.putExtra(Intent.EXTRA_STREAM, tmpFileUri); context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image))); 

Ahora, estoy atascado en cómo compartir un archivo que se encuentra en una carpeta privada. He utilizado el código proporcionado por la documentación de google:

 <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.test.myapp.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> ... ... <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="internal_files" path="/"/> <cache-path name="internal_cache" path="/" /> </paths> 

Éste es el código para compartir archivos utilizando FileProvider pero no funciona con ninguna aplicación, excepto lo que pasa:

 final File tmpFile = new File(context.getCacheDir(), "exported.jpg"); Uri tmpFileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", tmpFile); //Remove the uri permission because we overwrite the file context.revokeUriPermission(tmpFileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); saveBitmapToPath(bitmap, tmpFile); bitmap.recycle(); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setDataAndType(tmpFileUri, "image/jpeg"); shareIntent.putExtra(Intent.EXTRA_STREAM, tmpFileUri); //Grant again the permissions shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image))); 

¿Por qué sigo recibiendo errores en otras aplicaciones, como esto:
java.lang.SecurityException: Permission Denial: content://com.test.myapp.fileprovider/internal_cache/exported.jpg (pid=675, uid=10052) requires null
O
IllegalArgumentException: Failed to find configuration root that contains content://com.test.myapp.fileprovider/internal_cache/exported.jpg

Finalmente, mirando el código fuente de la aplicación receptora, tengo la solución.
Este es el código de trabajo completo que comparto.
Espero ayudar a alguien:

 <!-- AndroidManifest.xml --> <provider android:name="com.test.myapp.fileprovider.FileProvider" android:authorities="com.test.myapp.fileprovider" android:exported="true" tools:ignore="ExportedContentProvider" /> 

 //EntryPoint private void mySharer() { ArrayList<Uri> streamUris = new ArrayList<Uri>(); for (int i = 0; i < 10; i++) { File tmpFile = new File(getContext().getCacheDir(), "tmp" + i + ".jpg"); Uri tmp = FileProvider.getUriForFile("com.test.myapp.fileprovider", tmpFile); streamUris.add(tmp); } } //Share Intent creator public final void shareUris(ArrayList<Uri> streamUris) { if (!streamUris.isEmpty()) { Intent shareIntent = new Intent(); shareIntent.putExtra(ShareCompat.EXTRA_CALLING_PACKAGE, getPackageName()); shareIntent.putExtra(ShareCompat.EXTRA_CALLING_ACTIVITY, getComponentName()); shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_GRANT_READ_URI_PERMISSION); shareIntent.setType("image/jpeg"); if (streamUris.size() == 1) { shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, streamUris.get(0)); } else { shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE); shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, streamUris); } //For multiple images copy all images in the baseDir and use startActivityForResult startActivityForResult(Intent.createChooser(shareIntent, getString(R.string.share_image)), 500); } } //onResult you can delete all temp images/files with specified extensions protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case 500: getContentResolver().delete(FileProvider.getUriForFile(getPackageName() + ".fileprovider", null), FileProvider.WHERE_EXTENSION, new String[]{"jpg"}); break; default: break; } } /** * This class extends the ContentProvider */ abstract class AbstractFileProvider extends ContentProvider { private final static String OPENABLE_PROJECTION_DATA = "_data"; private final static String[] OPENABLE_PROJECTION = { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, OPENABLE_PROJECTION_DATA }; @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (projection == null) { projection = OPENABLE_PROJECTION; } final MatrixCursor cursor = new MatrixCursor(projection, 1); MatrixCursor.RowBuilder b = cursor.newRow(); for (String col : projection) { if (OpenableColumns.DISPLAY_NAME.equals(col)) { b.add(getFileName(uri)); } else if (OpenableColumns.SIZE.equals(col)) { b.add(getDataLength(uri)); } else if (OPENABLE_PROJECTION_DATA.equals(col)) { b.add(getFileName(uri)); } else { b.add(null); } } return cursor; } @Override public String getType(Uri uri) { return URLConnection.guessContentTypeFromName(uri.toString()); } protected String getFileName(Uri uri) { return uri.getLastPathSegment(); } protected long getDataLength(Uri uri) { return AssetFileDescriptor.UNKNOWN_LENGTH; } @Override public Uri insert(Uri uri, ContentValues initialValues) { throw new RuntimeException("Operation not supported"); } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { throw new RuntimeException("Operation not supported"); } @Override public int delete(Uri uri, String where, String[] whereArgs) { throw new RuntimeException("Operation not supported"); } } /** * This class extends the AbstractFileProvider */ public class FileProvider extends AbstractFileProvider { public static final String CONTENT_URI = "content://"; private File baseDir; @Override public boolean onCreate() { baseDir = getContext().getCacheDir(); if (baseDir != null && baseDir.exists()) { return true; } Log.e("FileProvider", "Can't access cache directory"); return false; } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { File f = new File(baseDir, uri.getPath()); if (f.exists()) { return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); } throw new FileNotFoundException(uri.getPath()); } @Override protected long getDataLength(Uri uri) { File f = new File(baseDir, uri.getPath()); return f.length(); } public static Uri getUriForFile(String authority, File file) { return Uri.parse(CONTENT_URI + authority + "/" + file.getName()); } } 

————- EDIT: 05/11/16 ————–
Añadido soporte para múltiples imágenes:

  1. Copiar todas las imágenes en la carpeta baseDir
  2. Implementar el método delete() en el FileProvider
  3. Utilizar startActivityForResult
  4. Escucha onActivityResult
  5. Ahora puede borrar todas las imágenes temporales

Para el archivo adjunto de correo electrónico, debe esperar a que se envíe el correo electrónico antes de eliminar el archivo; de lo contrario, enviará un archivo adjunto vacío

  • Cómo utilizar -assumenosideeffects clase android.util.Log en mi aplicación
  • Android: establece sólo un relleno de textview mediante programación
  • Cómo establecer el texto Min (obligatorio) y el texto máximo en edittext
  • Repetir la llamada API con retrofit y rxjava
  • Programáticamente eliminar SMS no funciona
  • ¿El cliente REST de Android, Sample?
  • Comercio con método de declaración sincronizado en java?
  • C ++ vs Java en Android
  • No se pueden eliminar contactos de la tarjeta SIM
  • En el oyente de cambio de fecha
  • Youtube API v3 buscar videos, recuperar el título y url
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.