How to filter the results of a query with buildChildDocumentsUriUsingTree
Asked Answered
G

2

9

I want to save a file on a SD card folder.

And I can't use V4 support on my project.

So I call:

Intent itent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
itent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
itent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(itent, requestCodeTree);

Then on the onActivityResult, I have:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    if (resultCode == RESULT_OK) {
        switch(requestCode) {
            case requestCodeTree:
                saveFile(intent.getData());
                break;
        }
    }
}

And the code for saveFile is:

   private void saveFile(Uri data) {
        ContentResolver contentResolver = context.getContentResolver();

        InputStream in = null;
        OutputStream out = null;

        try {

            // Problems start here ************************
            Uri toUriFile= getUriBackupFile(context, data);
            // ********************************************

            if (toUriFile==null) {
                Uri toUriFolder = DocumentsContract.buildDocumentUriUsingTree(data, DocumentsContract.getTreeDocumentId(data));
                toUriFile = DocumentsContract.createDocument(contentResolver, toUriFolder, "", backupName);
            }

            out = contentResolver.openOutputStream(toUriFile);
            in = new FileInputStream(fromFile);

            byte[] buffer = new byte[1024];
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
            in.close();
            // write the output file (the file is now copied)
            out.flush();
            out.close();

        } catch (FileNotFoundException e) {
            Log.e(TAG, "Failed", e);
        } catch (Exception e) {
            Log.e(TAG, "Failed", e);
        }
    }

So far so good.

Problems start when I call getUriBackupFile to get the uri of the destination file.

To do that, I query the ContentResolver with buildChildDocumentsUriUsingTree and try to filter the result where DocumentsContract.Document.COLUMN_DISPLAY_NAME matches my file's display name, like this:

private static Uri getUriBackupFile(Context context, Uri treeUri) {
        final ContentResolver resolver = context.getContentResolver();

        final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
                treeUri,
                DocumentsContract.getTreeDocumentId(treeUri));

        Cursor c = null;
        try {
            String[] projections = new String[] {
                    DocumentsContract.Document.COLUMN_DOCUMENT_ID,
                    DocumentsContract.Document.COLUMN_DISPLAY_NAME};

            // this line doesn't seem to have any effect !
        String selection = DocumentsContract.Document.COLUMN_DISPLAY_NAME + " = '" + backupName + "' ";
            // *************************************************************************

            c = resolver.query(childrenUri, projections, selection, null, null);

            if (c!=null && c.moveToFirst()) {
            // Here I expect to have c.getCount() == 1 or == 0
            // But actually c.getCount() == [Number of children in the treeUri] regardless of the selection
                String documentId = c.getString(0);
                Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri,
                        documentId);
                return documentUri;
            }

        } catch (Exception e) {
            Log.w(TAG, "Failed query: " + e);
        } finally {
            if (c!=null) c.close();
        }

        return null;
    }

But the query always return all the children of the treeUri, regardless of the selection. So, it seems the selection has no effect.

I could always loop through all the results, but if the selected folder has a large number of files it won't be good for the performance.

So my questions are:

  1. How I can filter the result of my query?
  2. Is this even the right approach to save a file on a sd card path?
Garbanzo answered 11/10, 2018 at 23:30 Comment(0)
I
1

The file system provider doesn't really support filtering:

https://github.com/aosp-mirror/platform_frameworks_base/blob/003ab94333bd6d47b35d7c241136d54b86a445b9/core/java/com/android/internal/content/FileSystemProvider.java#L370

The only choice is to get all rows and filter yourself.

Intoxicate answered 6/4, 2019 at 20:19 Comment(1)
+1 This is unfortunately correct. I keep discovering how badly documented and implemented the DocumentsContract is. And the new Storage Access Framework makes it mandatory....Psychologism
S
0
  1. You need to add permission to read/write external storage in your manifest. Add this line before your application tag.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  1. If your app is running on device with android 6 or higher. you need to request permission before you can write to sd card/external storage. Follow this documentation on checking permissions at run time.

  2. Get external directory by using Environment.getExternalStorageDirectory(). This will return external directory.

Also refer to this documentation (https://developer.android.com/training/data-storage/files) and this question.

Sarnen answered 24/10, 2018 at 9:10 Comment(2)
Thank you for your response. But my app already has these permissions and check for them at runtimeGarbanzo
ok. So you want to manually select the path where you want to save file?Sarnen

© 2022 - 2024 — McMap. All rights reserved.