Search code examples
javaandroiduridocumentfile

DocumentFile.exists() on a path that doesn't yet exist always returns true due to DocumentFile().fromTreeUri()


I'm trying to check if a file exists prior to creating it, using DocumentFile (due to the Storage Access Framework). But DocumentFile().fromTreeUri() removes the non-existant portion of the would-be Uri, which then causes DocumentFile().exists() to always return true, regardless of whether it exists or not.

I've created a simple example to demonstrate my point. First we ask the user to select a directory:

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // Ask the user for the source folder
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(intent, 100);
}

On response, we add /fictionalFile to the path (thus making it a non-existent file), and then check if it exists:

public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
    if (resultCode == RESULT_OK)
    {
        if(requestCode == 100)
        {
            Uri fictionalURI = Uri.parse(resultData.getData()+"/fictionalFile");
            DocumentFile fictionalFile = DocumentFile.fromTreeUri(this, fictionalURI);
            Log.i("STORAGE", "FICTIONAL URI: "+fictionalURI);
            Log.i("STORAGE", "FICTIONAL DOCUMENTFILE URI: "+fictionalFile.getUri());

            if(fictionalFile.exists())
            {
                Log.i("STORAGE", "Fictional file exists");
            }
        }
    }
}

However, when DocumentFile.fromTreeUri() is run on the fictional Uri, the fake "/fictionalfile" portion is lost, which then causes the DocumentFile.exists() function to return true, as shown by the below LogCat:

I/STORAGE: FICTIONAL URI: content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/fictionalFile

I/STORAGE: FICTIONAL DOCUMENTFILE URI: content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/document/17FA-1C18%3AFileSync%2Ftarget

I/STORAGE: Fictional file exists

(In the above example, I'm using the SD card, thus the long path names)

Is there another way to check if a not-yet created DocumentFile exists? The use case is that when copying a file from Directory A to Directory B, I want to check if said file already exists in Directory B prior to starting the transfer.

UPDATE: I've now realised that using DocumentFile.fromTreeUri() is wrong, and that I should be using DocumentFile.fromSingleUri(). This helps, but upon running .exists() on the new file I am getting W/DocumentFile: Failed query: java.lang.UnsupportedOperationException: Unsupported Uri content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/fictionalFile. Any thoughts?

public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
    if (resultCode == RESULT_OK)
    {
        if(requestCode == 100)
        {
            Uri fictionalURI = Uri.parse(resultData.getData()+"/fictionalFile");
            DocumentFile fictionalFile = DocumentFile.fromSingleUri(this, fictionalURI);
            Log.i("STORAGE", "FICTIONAL URI: "+fictionalURI);
            Log.i("STORAGE", "FICTIONAL DOCUMENTFILE URI: "+fictionalFile.getUri());

            if(fictionalFile != null && fictionalFile.exists())
            {
                Log.i("STORAGE", "Fictional file exists");
            }
        }
    }
}

Solution

  • Given treeUri as being the Uri returned by ACTION_OPEN_DOCUMENT_TREE, wrap treeUri in a DocumentFile using fromTreeUri(), then call findFile() on that DocumentFile supplying the display name that you are looking for (e.g., fictionalFile). If it returns null, there is no file matching that display name.

    IOW:

    if (DocumentFile.fromTreeUri(this, treeUri).findFile(whatevs) == null) {
      // TODO: something
    }
    

    Note, though, that "display name" is not necessarily a filename.