Android Share Intent para un bitmap: ¿es posible no guardarlo antes de compartirlo?

Intento exportar un bitmap desde mi aplicación utilizando la intención de compartir sin guardar un archivo para una ubicación temporal. Todos los ejemplos que encontré son de dos pasos 1) guardar en la tarjeta SD y crear Uri para ese archivo 2) iniciar la intención con este URI

¿Es posible hacerlo sin requerir el permiso WRITE_EXTERNAL_STORAGE, guardando el archivo [y quitándolo después]? ¿Cómo abordar dispositivos sin ExternalStorage?

Yo tuve el mísmo problema. No quería tener que pedir permiso de lectura y escritura de almacenamiento externo. Además, a veces hay problemas cuando los teléfonos no tienen tarjetas SD o las tarjetas se desmontan.

El siguiente método usa un ContentProvider llamado FileProvider . Técnicamente, aún está guardando el bitmap (en el almacenamiento interno) antes de compartirlo, pero no necesita solicitar ningún permiso. Además, cada vez que comparte el bitmap, el archivo de imagen se sobrescribe. Y dado que está en la memoria caché interna, se eliminará cuando el usuario desinstale la aplicación. Entonces, en mi opinión, es tan bueno como no guardar la imagen. Este método también es más seguro que guardarlo en un almacenamiento externo.

La documentación es bastante buena (ver la Lectura Adicional a continuación), pero algunas partes son un poco complicadas. Aquí hay un resumen que funcionó para mí.

Configurar el FileProvider en el Manifiesto

 ...  ...    ...   

Reemplace com.example.myapp con el nombre del paquete de su aplicación.

Crea res / xml / filepaths.xml

   

Comparte la imagen

 File imagePath = new File(context.getCacheDir(), "images"); File newFile = new File(imagePath, "image.png"); Uri contentUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider", newFile); if (contentUri != null) { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file shareIntent.setDataAndType(contentUri, getContentResolver().getType(contentUri)); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); startActivity(Intent.createChooser(shareIntent, "Choose an app")); } 

Otras lecturas

  • FileProvider
  • Opciones de almacenamiento - Almacenamiento interno
  • Compartir archivos
  • Guardando archivos

Intento exportar un bitmap desde mi aplicación utilizando la intención de compartir sin guardar un archivo para una ubicación temporal.

En teoría, esto es posible. En la práctica, probablemente no sea posible.

En teoría, todo lo que necesita compartir es un Uri que se resolverá en el bitmap. El enfoque más simple es si ese es un archivo al que la otra aplicación puede acceder directamente, como en el almacenamiento externo.

Para no escribirlo en absoluto, necesitaría implementar su propio ContentProvider , descubrir cómo implementar openFile() para devolver su bitmap en memoria, y luego pasar un Uri que represente ese bitmap en la Intent ACTION_SEND . Como openFile() necesita devolver un ParcelFileDescriptor , no sé cómo lo haría sin una representación en disco, pero no he dedicado mucho tiempo a buscar.

¿Es posible hacerlo sin requerir el permiso WRITE_EXTERNAL_STORAGE, guardando el archivo [y quitándolo después]?

Si simplemente no lo quiere en el almacenamiento externo, puede ir a la ruta ContentProvider , usando un archivo en el almacenamiento interno. Este proyecto de ejemplo muestra un ContentProvider que sirve un archivo PDF a través de ACTION_VIEW a un visor de PDF en un dispositivo; el mismo enfoque podría usarse para ACTION_SEND .

Esto para compartir CardView como una Imagen y luego guardarlo en el subdirectorio de caché del área de almacenamiento interno de la aplicación. Espero que sea de ayuda.

  @Override public void onClick(View view) { CardView.setDrawingCacheEnabled(true); CardView.buildDrawingCache(); Bitmap bitmap = CardView.getDrawingCache(); try{ File file = new File(getContext().getCacheDir()+"/Image.png"); bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file)); Uri uri = FileProvider.getUriForFile(getContext(),"com.mydomain.app", file); Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType("image/jpeg"); getContext().startActivity(Intent.createChooser(shareIntent, "Share")); }catch (FileNotFoundException e) {e.printStackTrace();} } }); 

Aquí está el método de trabajo para hacer una captura de pantalla de la propia aplicación y compartirla como imagen a través de cualquier messanger o cliente de correo electrónico.

Para arreglar el bitmap que no actualizaba el problema, mejoré la respuesta de Suragch , usando el comentario de Gurupad Mamadapur y añadí modificaciones propias.


Aquí hay un código en lenguaje Kotlin :

 private lateinit var myRootView:View // root view of activity @SuppressLint("SimpleDateFormat") private fun shareScreenshot() { // We need date and time to be added to image name to make it unique every time, otherwise bitmap will not update val sdf = SimpleDateFormat("yyyyMMdd_HHmmss") val currentDateandTime = sdf.format(Date()) val imageName = "/image_$currentDateandTime.jpg" // CREATE myRootView = window.decorView.rootView myRootView.isDrawingCacheEnabled = true myRootView.buildDrawingCache(true) // maybe You dont need this val bitmap = Bitmap.createBitmap(myRootView.drawingCache) myRootView.isDrawingCacheEnabled = false // SAVE try { File(this.cacheDir, "images").deleteRecursively() // delete old images val cachePath = File(this.cacheDir, "images") cachePath.mkdirs() // don't forget to make the directory val stream = FileOutputStream("$cachePath$imageName") bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream) // can be png and any quality level stream.close() } catch (ex: Exception) { Toast.makeText(this, ex.javaClass.canonicalName, Toast.LENGTH_LONG).show() // You can replace this with Log.e(...) } // SHARE val imagePath = File(this.cacheDir, "images") val newFile = File(imagePath, imageName) val contentUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", newFile) if (contentUri != null) { val shareIntent = Intent() shareIntent.action = Intent.ACTION_SEND shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file shareIntent.type = "image/jpeg" // just assign type. we don't need to set data, otherwise intent will not work properly shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri) startActivity(Intent.createChooser(shareIntent, "Choose app")) } } 

Si alguien todavía está buscando una solución fácil y corta sin ningún permiso de almacenamiento (admite también el turrón 7.0). Aquí está.

Añadir esto en Manifiesto

    

Ahora crea provider_paths.xml

    

Finalmente, agregue este método a su actividad / fragmento (rootView es la vista que desea compartir)

  private void ShareIt(View rootView){ if (rootView != null && context != null && !context.isFinishing()) { rootView.setDrawingCacheEnabled(true); Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache()); if (bitmap != null ) { //Save the image inside the APPLICTION folder File mediaStorageDir = new File(AppContext.getInstance().getExternalCacheDir() + "Image.png"); try { FileOutputStream outputStream = new FileOutputStream(String.valueOf(mediaStorageDir)); bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); outputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (ObjectUtils.isNotNull(mediaStorageDir)) { Uri imageUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", mediaStorageDir); if (ObjectUtils.isNotNull(imageUri)) { Intent waIntent = new Intent(Intent.ACTION_SEND); waIntent.setType("image/*"); waIntent.putExtra(Intent.EXTRA_STREAM, imageUri); startActivity(Intent.createChooser(waIntent, "Share with")); } } } } }