35

How To Share Files With Android FileProvider

 3 years ago
source link: https://vladsonkin.com/how-to-share-files-with-android-fileprovider/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Sharing is an essential feature of almost any app. Besides the simple data, we can also share the files, and Android FileProvider is a great helper here. In this article, we will see why we need a FileProvider and how to share your files with other apps.

Why we need Android FileProvider

Let’s imagine we have a budget app. User types in his income/expenses, and the app shows it with some beautiful graphs and stores the data as a CSV file inside the app directory.

The essential feature is to give a user the possibility to share their raw CSV data, so the user can open it in Google Sheets or any other app. Our first attempt can be to create an implicit android intent and send the file as an extra:

val shareIntent = Intent().apply {
 action = Intent.ACTION_SEND
 putExtra(Intent.EXTRA_STREAM, csvUri)
 type = "text/csv"
}
startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to)))

But it will not work because our receiving application (for example, Google Sheets) doesn’t have permission to access this file, which is located inside our budget app directory. 

In this situation, we have two options:

  1. Add our CSV file to the common MediaStore. The downside is that the file will be available to any app, and it’ll not work in our case because we have sensitive data.
  2. Use a ContentProvider, which will ensure that the receiving applications have the correct permissions to access the needed file. And an easy way to create this ContentProvider is by using an Android FileProvider.

Android FileProvider Example

Android FileProvider is a subclass of ContentProvider and allows us to share files more securely by creating a content:// Uri instead of file:// Uri. It’s more secure because we expose only the file’s content, and the actual location of the file is hidden.

FileProvider is actually a ContentProvider, and we start by adding it to the AndroidManifest.xml:

<application
  ...
  <provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.vladsonkin.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/provider_paths"/>
  </provider>
</application>

Let’s break it line-by-line:

  1. android:name refers to a FileProvider class from AndroidX.
  2. android:authorities is a unique value that helps the Android system to distinguish different providers. Usually, you create it by reversing your domain name and adding a “fileprovider” at the end.
  3. android:exported flag controls if this provider is public or not. If it’s public, then all other apps can access this provider without any permissions, which is not needed in our case.
  4. android:grantUriPermissions attribute allows us to grant temporary access to the files.
  5. <meta-data> element specifies the file in which we describe the shared paths for this provider.

Now we can create the provider_paths.xml under the app/res/xml directory and specify the shared paths:

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <files-path name="share" path="external_files"/>
</paths>

The element inside the <paths> can be different and the usage depends on the location of your file:

  1. <files-path> is the files/ directory in the app’s internal storage. 
  2. <cache-path> is the cache directory in the app’s internal storage. 
  3. <external-path> is the root of external storage. 
  4. <external-files-path> is the root of your app’s external storage.
  5. <external-cache-path> is the root of your app’s external cache storage.
  6. <external-media-path> is the root of your app’s external media storage.

In our example CSV budget file is located in the internal storage, so that’s why I used a <files-path>. This element also has two attributes: 

  1. name can increase security by replacing the URI subdirectory with a given value.
  2. path specifies the subdirectory that you want to share.

Note: we can share only directories and not individual files.

At this point, our FileProvider is set and we finally can share the file:

val csvUri = FileProvider.getUriForFile(this, getPackageName(), csvFile)
val intent = ShareCompat.IntentBuilder.from(this)
  .setStream(csvUri)
  .setType("text/csv")
  .intent
  .setAction(Intent.ACTION_SEND)
  .setDataAndType(csvUri, "text/csv")
  .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)

Here we get the file Uri with the help of FileProvider, create the share intent, and launch it. The chosen receiver app has permission to work with the file, and it doesn’t know it’s location.

Summary

We can share the files even if they are located under the app directory. Android FileProvider makes it really easy, and for this, you only need to specify it in your app manifest.

It’s also considered a secure approach because the file’s location is hidden, and only you decide when and where to share the files.

What do you think about the FileProvider? I would love to see your comments below.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK