Google durante el tiempo a variado la API de acceso a los archivos del sistema Android, a cada versión a aumentado la seguridad hasta llegar a tal punto que no permite acceder al sistema propio de archivos como se hacia antés, pero por otro lado ofrece una API solida mediante proveedores de contenido.
Como el usuario participa en la selección de los archivos o directorios a los que puede acceder la app, este mecanismo no requiere ningún permiso del sistema
Crear un archivo
Para crear un archivo usando ContentProvider
private fun createFile(fileName:String = "sample.txt",mineType: String = "text/plain") {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = mineType
putExtra(Intent.EXTRA_TITLE, fileName)
}
startActivityForResult(intent, PICK_CREATE_FILE)
}
Su uso es llamar la función createFile con el nombre del archivo a crear y el tipo de contenido que tendra, en este caso texto plano su minetype es text/plain
createFile("demo.txt", "text/plain")
El mismo sistema android se encargará de mostrar el selector de archivo para así poderlo guardar en el directorio correspondiente, hasta poderlo subir a GoogleDrive
Al crear un archivo el selector de archivos nos devolvera una uri del archivo donde se encuentra, no confundir uri con el path (obsoleto), el uri del archivo creador se puede obtener con la devuelta en onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
PICK_CREATE_FILE -> {
data?.data?.also { uri ->
//uri del archivo creado
}
}
}
}
}
Se obtiene el uri para operar con el ContenProvider, para escribir contenido ver sección Modificar un archivo para saber como añadir contenido dentro de el archivo.
Seleccionar un archivo
Para abrir un archivo con ContenProvider, debemos invocar el selector de archivos por parte del sistema android y que nos devuelve el uri.
private fun openFile(mineType: String = "*/*") {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = mineType
}
startActivityForResult(intent, PICK_OPEN_FILE)
}
Con el método openFile podemos abrir el selector nativo que nos ofrece el sistema Android, se puede indicar solamente el tipo de archivo que requerimos, en este caso como se trabaja con texto plano text/plain
openFile("text/plain")
Luego de selecionar el archivo, nos devolverá su uri en onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
...
PICK_OPEN_FILE -> {
data?.data?.also { uri ->
//uri del archivo selecionado
}
}
}
}
Tras obtener el uri del archivo podemos operar con el ContentProvider, para recuperar la información de un archivo ver sección Leer un archivo
Modificar un archivo
Para añadir contenido aun archivo usando ContentProvider
@Throws(IOException::class)
private fun writeTextToUri(uri: Uri, content: String) {
requireActivity().contentResolver.openFileDescriptor(uri, "w")?.use { it ->
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
fileOutputStream.write(
content.toByteArray()
)
fileOutputStream.close()
}
}
}
Su uso writeTextToUri le indicamos el uri del archivo y el contenido a añadir
writeTextToUri(uri,"eso es contenido de prueba usando ContentProvider de Android")
Si usamos una app de gestión de archivos podemos comprobar si se a creado el archivo y su contenido correctamente. Para controlar si a ocurrido un error al querer añadir contenido al archivo, se puede recurrir a encapsular con try..catch IOException
PICK_CREATE_FILE -> {
data?.data?.also { uri ->
try {
writeTextToUri(uri,"eso es contenido de prueba usando ContentProvider de Android")
} catch ( e : IOException) {
e.printStackTrace()
}
}
}
Leer un archivo
Para recuperar el contenido de un archivo usando ContentProvider
@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
val stringBuilder = StringBuilder()
requireActivity().contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { reader ->
var line: String? = reader.readLine()
while (line != null) {
stringBuilder.append(line)
line = reader.readLine()
}
reader.close()
}
}
return stringBuilder.toString()
}
Su uso readTextFromUri le indicamos el uri del archivo y nos devolverá la cadena de texto con el contenido.
val contenido = readTextFromUri(uri)
En caso de comprobar si el archivo existe justo el momento de leer su contenido, encapsular el código dentro de un bloque try…catch…IOException
PICK_OPEN_FILE -> {
data?.data?.also { uri ->
try {
val contenido = readTextFromUri(uri)
} catch ( e : IOException) {
e.printStackTrace()
}
}
}