Comment utiliser le support FileProvider pour partager du contenu avec d’autres applications?

Je cherche un moyen de partager correctement (pas OUVERT) un fichier interne avec une application externe en utilisant FileProvider de la bibliothèque Android Support.

À l’instar des documents,

   

et en utilisant ShareCompat pour partager un fichier avec d’autres applications comme suit:

 ShareCompat.IntentBuilder.from(activity) .setStream(uri) // uri from FileProvider .setType("text/html") .getIntent() .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 

ne fonctionne pas, car FLAG_GRANT_READ_URI_PERMISSION n’accorde l’autorisation que pour l’URI spécifié sur les data de l’intention, et non la valeur de l’extra EXTRA_STREAM (définie par setStream ).

J’ai essayé de compromettre la sécurité en définissant android:exported sur true pour le fournisseur, mais FileProvider vérifie en interne si elle-même est exscope, quand c’est le cas, elle génère une exception.

A l’aide de FileProvider partir de la bibliothèque de support, vous devez accorder et révoquer manuellement les permissions (à l’exécution) pour que les autres applications puissent lire des Uri spécifiques. Utilisez les méthodes Context.grantUriPermission et Context.revokeUriPermission .

Par exemple:

 //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); 

En dernier recours, si vous ne pouvez pas fournir le nom du package, vous pouvez accorder l’autorisation à toutes les applications pouvant gérer une intention spécifique:

 //grant permisions for all apps that can handle given intent Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); ... List resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { Ssortingng packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } 

Méthode alternative selon la documentation :

  • Placez l’URI de contenu dans une intention en appelant setData ().
  • Ensuite, appelez la méthode Intent.setFlags () avec FLAG_GRANT_READ_URI_PERMISSION ou FLAG_GRANT_WRITE_URI_PERMISSION ou les deux.
  • Enfin, envoyez l’intention à une autre application. Le plus souvent, vous faites cela en appelant setResult ().

    Les permissions accordées dans une intention restnt en vigueur tant que la stack de l’activité récepsortingce est active. Lorsque la stack se termine, le
    les permissions sont automatiquement supprimées. Autorisations accordées à un
    L’activité dans une application cliente est automatiquement étendue à d’autres applications.
    composants de cette application.

Btw. Si nécessaire, vous pouvez copier la source de FileProvider et modifier la méthode attachInfo pour empêcher le fournisseur de vérifier s’il est exporté.

Cette solution fonctionne pour moi depuis OS 4.4. Pour que cela fonctionne sur tous les appareils, j’ai ajouté une solution de contournement pour les anciens appareils. Cela garantit que la solution la plus sûre est toujours utilisée.

Manifest.xml:

     

file_paths.xml:

    

Java:

 public static void sendFile(Context context) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); Ssortingng 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 resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { Ssortingng 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); } } 

L'autorisation est révoquée avec revokeFileReadPermission () dans les méthodes onResume et onDestroy () du fragment ou de l'activité.

Exemple de code de travail complet sur le partage de fichiers depuis le dossier interne de l’application. Testé sur Android 7 et Android 5.

AndroidManifest.xml

  ....     

xml / provider_paths

     

Code lui-même

  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); 

Puisque, comme le dit Phil dans son commentaire sur la question initiale, ceci est unique et qu’il n’ya pas d’autres informations sur SO on google, je pensais que je devais également partager mes résultats:

Dans mon application, FileProvider est sorti de la boîte pour partager des fichiers en utilisant l’intention de partage. Il n’y avait pas de configuration ou de code spécial nécessaire, au-delà de la configuration du FileProvider. Dans mon fichier manifest.xml, j’ai placé:

     

Dans my_paths.xml j’ai:

    

Dans mon code j’ai:

  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.ssortingng.share_file))); 

Et je suis capable de partager mes fichiers de stockage dans mes applications de stockage privé avec des applications telles que Gmail et Google Drive sans aucun problème.

Autant que je sache, cela ne fonctionnera que sur les nouvelles versions d’Android, vous devrez donc probablement trouver un autre moyen de le faire. Cette solution fonctionne pour moi sur la version 4.4, mais pas sur la version 4.0 ou 2.3.3. Ce ne sera donc pas un moyen utile de partager du contenu pour une application destinée à être exécutée sur un appareil Android.

Dans manifest.xml:

    

Prenez note de la façon dont vous spécifiez les autorités. Vous devez spécifier l’activité à partir de laquelle vous allez créer l’URI et lancer l’intention de partage. Dans ce cas, l’activité s’appelle SharingActivity. Cette exigence n’est pas évidente dans les documents de Google!

file_paths.xml:

     

Faites attention à la façon dont vous spécifiez le chemin. La valeur par défaut ci-dessus correspond à la racine de votre stockage interne privé.

Dans 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")); 

Dans cet exemple, nous partageons une image JPEG.

Enfin, c’est probablement une bonne idée de vous assurer que vous avez correctement enregistré le fichier et que vous pouvez y accéder avec quelque chose comme ceci:

 File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg"); if(myFile != null){ Log.d(TAG, "File found, file description: "+myFile.toSsortingng()); }else{ Log.w(TAG, "File not found!"); } 

Dans mon application, FileProvider fonctionne très bien et je suis capable de joindre des fichiers internes stockés dans le répertoire des fichiers aux clients de messagerie tels que Gmail, Yahoo, etc.

Dans mon manifeste comme mentionné dans la documentation Android, j’ai placé:

    

Et comme mes fichiers étaient stockés dans le répertoire des fichiers racine, le fichier paths.xml était comme suit:

    

Maintenant dans le code:

  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 Ssortingng[] {"email-address you want to send the file to"}); Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider", file); ArrayList uris = new ArrayList(); 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(); } } 

Cela a fonctionné pour moi. J’espère que ça aide 🙂

juste pour améliorer la réponse donnée ci-dessus: si vous obtenez NullPointerEx:

vous pouvez également utiliser getApplicationContext () sans contexte

  List resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { Ssortingng packageName = resolveInfo.activityInfo.packageName; grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } 

Si vous obtenez une image de la caméra, aucune de ces solutions ne fonctionne pour Android 4.4. Dans ce cas, il est préférable de vérifier les versions.

 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); } 

Je veux partager quelque chose qui nous a bloqués pendant quelques jours: le code du fournisseur de fichier DOIT être inséré entre les balises de l’application, pas après. C’est peut-être sortingvial, mais ce n’est jamais spécifié, et j’ai pensé que j’aurais pu aider quelqu’un! (merci encore à piolo94)