How to set the original uri of a file picker using ActivityResultLauncher

16 Views Asked by At

I am using a file picker to let the user choose a data input file (.txt). After some research, I finally found a solution that avoids any deprecated method, using ActivityResultLauncher:

Declaring the filePicker with:

private var filePicker: ActivityResultLauncher<String>? = null

Registering the file picker in my fragment's onAttach method, calling:

private fun registerFilePicker() {
    filePicker = registerForActivityResult<String, Uri>(
        ActivityResultContracts.GetContent()
    ) { uri: Uri? -> onPickFile(uri) }
}

Opening the file picker with:

private fun openFilePicker() {
    val mimeType = "*/*"
    filePicker!!.launch(mimeType)
}

Triggering action on the user's selected file:

private fun onPickFile(uri: Uri?) {
    try {
        requireActivity().contentResolver.openInputStream(uri!!).use { inputStream ->
            inputStream?.let {
                it.reader().useLines { lines ->
                    lines.forEach {
                        val mot = it.substringBefore(',')
                        if (!(mot in Mots))
                            importListLine.add(Import(mot, it.substringAfter(','), true ))
                    }
                    adapter?.setImportList(importListLine)
                    adapter?.notifyDataSetChanged()
                    binding.btnSave.isVisible = true
                    binding.btnCancel.isVisible = true
                }
            }
        }

    } catch (exception: IOException) {
    }
}

This works pretty fine so far. But what I want now is to open the filepicker in my own directory uri, which should be feasible with something like:

putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)

Can someone tell where this should be added to ?

1

There are 1 best solutions below

0
Simon W On

It appears that to customize the intent (e.g. to define an initial uri to the picker), the best would be to override the createIntent method. I tried it but did not made it to a full proper functioning (my initial uri was ignored).

Meantime I found the following solution on medium.com by Abdul Hamid (thanks to him) that is both compact and works perfeclty for me:

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity

class YourActivity : AppCompatActivity() {

    private val requestFileLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                result.data?.data?.let { uri ->
                    handleSelectedFile(uri)
                }
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Replace "your_folder_name" with the actual folder name you want to open
        // as example: "Downloads", "Documents"        
        openSpecificFolder("your_folder_name")
    }

    private fun openSpecificFolder(folderName: String) {
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"  // Set the MIME type to filter files
            val uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:$folderName")
            putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)
        }

        requestFileLauncher.launch(intent)
    }

    private fun handleSelectedFile(uri: android.net.Uri) {
        val cursor = contentResolver.query(uri, null, null, null, null)
        cursor?.use {
            if (it.moveToFirst()) {
                val displayName = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
                // Do something with the file name
            }
        }
    }
}

If you are working in a fragment instead of main activity, just declare and initialize contentResolver like this:

val contentResolver = getActivity().getContentResolver()