The Storage Access Framework (SAF) provides a file picker that allows to browse, create, and open files hosted by storage services (document providers). It can be external storage, cloud-based storage, etc.
SAF doesn't require defining permissions in the manifest file because the user selects the files or directories that application can access. Files won't be removed when the user uninstalls the application.
In the layout XML file, we added two Button
elements. One of these will be used to create a file, another one to open a file. A TextView
element will be used to display a file content.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/createButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Create" />
<Button
android:id="@+id/openButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Open" />
<TextView
android:id="@+id/myTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
We can use the ACTION_CREATE_DOCUMENT
intent action to open the file picker that allows to select a location where to create a new file. When configuring the intent, we can specify a MIME type and name of the file. We cannot overwrite an existing file. A number in parentheses will be appended at the end of the file name, if the application tries to create a file with the same name.
We can open a file by using a file picker which was loaded by ACTION_OPEN_DOCUMENT
intent action. We can specify a MIME type to show only specific file types.
When the user selected a file, an intent returns control to the application by calling the onActivityResult()
method. The first parameter is the request code that is used to identify the intent. The second parameter is the result code that indicates whether the intent was successful. The third parameter is result data that contains the selected file's URI.
By using the URI of the selected file, we can write data to the file or read data from it.
package com.example.app
import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import java.io.FileInputStream
import java.io.FileOutputStream
class MainActivity : AppCompatActivity()
{
private val fileName: String = "my_file.txt"
private val createRequestCode: Int = 1
private val openRequestCode: Int = 2
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
createButton.setOnClickListener { createFile() }
openButton.setOnClickListener { openFile() }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?)
{
super.onActivityResult(requestCode, resultCode, resultData)
if (resultCode == Activity.RESULT_OK && resultData != null) {
if (requestCode == createRequestCode) {
write("Hello world", resultData.data as Uri)
} else if (requestCode == openRequestCode) {
myTextView.text = read(resultData.data as Uri)
}
}
}
private fun createFile()
{
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TITLE, fileName)
startActivityForResult(intent, createRequestCode)
}
private fun openFile()
{
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "text/plain"
startActivityForResult(intent, openRequestCode)
}
private fun write(message: String, uri: Uri)
{
val descriptor = contentResolver.openFileDescriptor(uri, "w")!!.fileDescriptor
val fileOut = FileOutputStream(descriptor)
fileOut.write(message.toByteArray())
fileOut.close()
}
private fun read(uri: Uri): String?
{
val descriptor = contentResolver.openFileDescriptor(uri, "r")!!.fileDescriptor
val fileIn = FileInputStream(descriptor)
val message = fileIn.readBytes().toString(Charsets.UTF_8)
fileIn.close()
return message
}
}
Leave a Comment
Cancel reply