Search code examples
javaandroidstorage-access-framework

How do I read a file based on a parameterised filename, given a URI of a user selected directory?


I am trying to build an Android app that allows a user to select a folder containing images, and from that point onwards, the app is able to load specific images by pre-determined image names placed in that user selected folder. The use case is enabling the app user to customise themes/images of the app by providing their own assets.

I can get the URI of the user selected folder ok. Assuming that the required image files with the required filenames will always be present in the user selected folder (I have implemented try-catch blocks to deal with this), how do I construct a Uri for the image file so I can use it (e.g. to set an ImageView?)

The user must be able to select from a range of locations for assets, including an SD card.

//Key variables
ImageView myImageView;
String myImageFilename = "foobar.png"; //image to be loaded
Uri imageUri;
Integer MYREQUESTID = 100000000;

//This is how the user selects a directory:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, MYREQUESTID);

//Use on activity result to capture the user selected folder;
URI userSelectedDirUri = data.getData();

Tried the following to append the filename onto the user selected directory, but it results in an invalid Uri. I notice that the Uri is encoded with %2F instead of /, but using this method the filename is appended with / and I can't work out how to use %2F instead

imageUri = userSelectedDirUri.buildUpon().appendPath(myImageFilename).build();

The following works, but the pickedDir.findFile(...) results in 50,000+ calls to the listFile() and findFile() method that is causing constant garbage collection. The user's folder may contain 100+ files.

DocumentFile pickedDir = DocumentFile.fromTreeUri(context, directoryUri);
DocumentFile theFile = pickedDir.findFile(myImageFilename);
imageUri = theFile.getUri();

The goal

myImageView.setContentUri(imageUri);

This last line of the code should update the ImageView myImageView with the image called myImageFilename, residing in the directory described by userSelectedDirUri.

Any help appreciated! I am also not implementing this as an asyncTask which I think I should be. Extra kudos/points/gratitude for examples using and not using asyncTask, but not essential (will work it out myself once the core logic is fixed).

Thanks!


Solution

  • Solved using the approach suggested by @CommonsWare.

    Hashmap<String, Uri> imageUriMap = new Hashmap<>; //Defined outside of this method
    
    public String indexImages(Uri userSelectedDirUri){
    
        ContentResolver contentResolver = this.getContentResolver(); //When this method is used in the context of an Activity.
        Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
                DocumentsContract.getTreeDocumentId(uri));
    
        Cursor c = null;
    
        c = contentResolver.query(childrenUri, new String[] {
                DocumentsContract.Document.COLUMN_DISPLAY_NAME,
                DocumentsContract.Document.COLUMN_DOCUMENT_ID}, null, null, null);
    
        Integer count = 0;
        try {
            while (c.moveToNext()) {
                final String documentName = c.getString(0);
                final String documentId = c.getString(1);
                final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(uri,
                        documentId);
                imageUriMap.put(documentName, documentUri);
                count++;
            }
        } finally {
            c.close();
    
        }
        return (count.toString() + " images indexed");
    }
    

    Then set images using:

    imageView.setImageUri(imageUriMap.get(myImageFilename));