• 8 hours
  • Hard

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 4/27/23

Share a file using FileProvider

Your users are delighted to be able to write everything that enters into their heads in their travel book! However, they would now like to be able to share this travel book; and more particularly, the one that is located in the internal storage, which is theoretically inaccessible to the user.

Discover FileProvider

To securely expose the content on Android to third-party apps (such as providing a file containing text. 😊), it is common to use the class ContentProvider. That class will generate a secure URI, allowing anyone who possesses it to access the resource that you’ve shared.

The class FileProvider is a child class inheriting from ContentProvider, allowing you to securely expose a resource of the type File, which is, of course, a file.  😊

In our case, we’ll use this class to share the file tripBook.txt located in our internal memory.

Define a FileProvider

To begin using a FileProvider, we declare an instance in the manifest of our application:

Excerpt from AndroidManifest.xml
<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.openclassrooms.savemytrip">

   ...
   
   <application
      ...>
      ...
      <!-- FileProvider - Expose File -->
      <provider
         android:name="android.support.v4.content.FileProvider"
         android:authorities="com.openclassrooms.savemytripkt.fileprovider"
         android:exported="false"
         android:grantUriPermissions="true">
         <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths" />
      </provider>
       ...
   </application>
</manifest>

Explanation: Here we've declared our FileProvider directly in XML in our application's manifest. No need for Java!

The declaration is made using the provider tag, which allows us to declare a content provider:

  • android:name: Corresponds to the name of the class that implements the content provider; here FileProvider from the androidx.core.content package.

  • android:authorities: Used to uniquely identify the authority that is exposing the data.

  • android:exported: Used to define whether the content provider can be accessed by applications other than ours. In our case, we want ours to be able to share content, but without other apps being able to access it.

  • android:grantUriPermissions: Used to allow or disallow actions that generally require special permissions, like the rights to read or write on a storage space, for example.

We've also defined the location we wish to expose using metadata tags. To do this, create in the folder res/ the sub-folder xml/, and place the following file into it:

File res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>

<paths>

   <files-path name="BookTrip" path="bookTrip/"/>

</paths>

Explanation: Here we indicate the folder in which we previously placed our tripBook.txt file, and that we wish to expose it.

And that’s it, the rest will happen in our TripBookActivity controller!

Share a file

First, we are going to add a method for retrieving a file from a defined location to our StorageUtils object to retrieve our tripBook.txt file from internal storage.

Excerpt from StorageUtils.kt: 

object StorageUtils {
   ...
    fun getFileFromStorage(rootDestination: File, 
                           context: Context,
                           fileName: String, 
                           folderName: String): File? {

        try {

            return createOrGetFile(rootDestination, fileName, folderName)

        }
        catch(e: NullPointerException) {
            Toast.makeText(context, context.getString(R.string.error_happened),
                         Toast.LENGTH_LONG).show()
        }
        return null
    }
   ...
}

 Explanation: We simply retrieve a file from a root directory passed as a parameter (rootDestination).

Now, let’s edit our TripBookActivity activity to share the file.

Excerpt from TripBookActivity.kt:
class TripBookActivity : BaseActivity() {
   ...
   // 1 - Define the authority of the FileProvider
   private val AUTHORITY = "com.openclassrooms.savemytripkt.fileprovider"
   ...
   
   override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_share -> {
                this.shareFile()
                return true
            }
          ...
        }
        return super.onOptionsItemSelected(item)
    }
   ...
   // ----------------------------------
   // SHARE FILE
   // ----------------------------------
   // 2 - Share the internal file
   private fun shareFile() {
        val internalFile = StorageUtils.getFileFromStorage(filesDir, this, FILENAME, FOLDERNAME)
        val contentUri = FileProvider.getUriForFile(applicationContext, AUTHORITY, internalFile!!)
        val sharingIntent = Intent(Intent.ACTION_SEND)
        sharingIntent.type = "text/*"
        sharingIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
        startActivity(Intent.createChooser(sharingIntent,
                    getString(R.string.trip_book_share)))
    }

Explanation: First, we created a variable (1) named  AUTHORITY  containing the identifier of our authority (the one that was declared in our manifesto). Then, we created a method (2) called  shareFile, which will be called as soon as the user clicks on the Share button (3) on the toolbar.

Let’s talk a little bit more about the method shareFile. First, it retrieves the file located in our internal storage space using the method previously created in our StorageUtils object. Next, using the method   FileProvider.getUriForFile, we are going to generate a secure URI (a unique identifier) pointing to our file.

Now that this URI has been generated, we will be able to create a sharing intent (Intent.ACTION_SEND), using that URI to share our file! 🙂

And that's all! Run the app and click on the Share button. You should now be able to share this file, even though it is stored in the internal memory of the application. 😉

Let's recap!

  •  To share a file located in the internal storage, you have to use a FileProvider.

Now you have everything you need to know to handle and share files in internal and external storage. Next we can examine a fundamental part of Android programming: database storage!

Example of certificate of achievement
Example of certificate of achievement