androidkotlinsaf

Is there a way to use SAF to access txt files in the same directory as images in an Android app?


I'm trying to load images in my app, in the format in trying to import, each image has a corresponding tag file. For instance cat.jpg has a corresponding cat.txt find that could contain "Cat, overweight, Felix, Home".

I get a list of file URI, that I'm adding to a list of pictures, and while loading those pictures, I'm checking for the existence of a text file, and it exists I add it to the list of tags of the picture.

All my attempts to access text files have been unsuccessful, it seems that there is always an issue with the URI of those.

I also tried to get the folder that contains the picture file, and list the files in it, but that was also unsuccessful.

I guess that dealing with this type of data is not very compliant with the SAF approach, but it would be better to keep this structure (image + tags in txt) in order to interact with other systems.

I'm using Android API 30 to test the app, if that is if any relevance. Here is the code I came with, it always result in file not found:


fun getPictures(fileUris: List<Uri>): List<Picture> {
val picturesList = mutableListOf<Picture>()
for (fileUri in fileUris) {
    val picture = Picture(fileUri, mutableListOf())

    // Generate a txt file name corresponding to the picture file
    val imageNameWithoutExtension = fileUri.lastPathSegment?.substringBeforeLast(".")
    val txtName = "$imageNameWithoutExtension.txt"

    val documentId = DocumentsContract.getDocumentId(fileUri)
    val parentDocumentId = documentId.substringBeforeLast(":")
    val txtDocumentId = "$parentDocumentId:$txtName"
    val txtFileUri = DocumentsContract.buildDocumentUriUsingTree(fileUri, txtDocumentId)

    val documentFile = DocumentFile.fromSingleUri(context, txtFileUri)
    var mimeType: String? = null
    if (documentFile != null && documentFile.exists()) {
        // File exists, now you can get the MIME type
        mimeType = documentFile.type
    }else{
        Log.d("MSG", "file not found")
    }

    // If the .txt file exists and its mime type is text, read the tags from it
    if (mimeType != null && isTextMimeType(mimeType)) {
        val tags = readTags(Picture(txtFileUri, mutableListOf()))
        picture.tags = tags
    }
    picturesList.add(picture)
}

Solution

  • Actually, getting the string of fileUri, and replace the extension with a "txt" is much a simpler and works.

    I don't know if it always generate correct URI, but that's the only way I got it working until now.

    fun getPictures(fileUris: List<Uri>): List<Picture> {
        val picturesList = mutableListOf<Picture>()
        for (fileUri in fileUris) {
            val picture = Picture(fileUri, mutableListOf())
    
            val txtFileUriString = fileUri.toString().substringBeforeLast(".") + ".txt"
            val txtFileUri = Uri.parse(txtFileUriString)
    
            val documentFile = DocumentFile.fromSingleUri(context, txtFileUri)
            var mimeType: String? = null
            if (documentFile != null && documentFile.exists()) {
                // File exists, now you can get the MIME type
                mimeType = documentFile.type
            } else {
                Log.d("MSG", "file not found")
            }
    
            // If the .txt file exists and its mime type is text, read the tags from it
            if (mimeType != null && isTextMimeType(mimeType)) {
                val tags = readTags(Picture(txtFileUri, mutableListOf()))
                picture.tags = tags
            }
            picturesList.add(picture)
        }
        return picturesList
    }