title: Android Q, get a list of files with MediaStore ContentResolver
body: <p>Per <a href="https://developer.android.com/training/data-storage/shared/documents-files#use-cases" rel="nofollow noreferrer">https://developer.android.com/training/data-storage/shared/documents-files#use-cases</a>, once I <code>targetSdk 29</code>, the only way to get a list of files from the /Downloads directory is to use <code>ACTION_OPEN_DOCUMENT_TREE</code>, specifically:</p>
  
  <pre><code>// Using the Uri returned from Intent.ACTION_OPEN_DOCUMENT_TREE
  DocumentFile dir = DocumentFile.fromTreeUri(context, uri);
  DocumentFile[] files = dir.listFiles();
  </code></pre>
  
  <p>Which is fine, but it requires some explicit user action to select and approve it. <em>(Though this will <a href="https://developer.android.com/preview/privacy/storage#file-directory-restrictions" rel="nofollow noreferrer">further get killed off if targetSdk 30/R</a>.., but that's besides the point for now)</em></p>
  
  <p>So how is <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.nbu.files" rel="nofollow noreferrer">https://play.google.com/store/apps/details?id=com.google.android.apps.nbu.files</a> able to still get a list of files from /Downloads without that explicit selection/approval? (it is also <code>targetSdk 29</code>)</p>
  
  <p>I tried these methods and they return empty results <code>"uri empty"</code>:</p>
  
  <pre><code>// 1. uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
  // 2. uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
  cursor = context.getContentResolver().query(
          uri, null, null, null,
          DocumentsContract.Document.COLUMN_DISPLAY_NAME + " DESC");
  if (cursor != null) {
      if (cursor.moveToFirst()) {  // targetSdk 28, I get these results
          final int MAX_RESULTS = 50;
          int colCount = cursor.getColumnCount();
          int rowCount = cursor.getCount();
          peek.append("uri[").append(rowCount).append(rowCount &gt;= MAX_RESULTS ? "+]" : "]");
          do {
              peek.append("\n=======================================");
              int colPos = cursor.getPosition();
              if (colPos &gt;= MAX_RESULTS) break;
              for (int i = 0; i &lt; colCount; i++) {
                  String colName = cursor.getColumnName(i);
                  String colVal = cursor.getString(i);
                  peek.append("\n").append(colPos).append(".").append(i).append(" ")
                          .append(colName).append("=").append(colVal);
              }
          } while (cursor.moveToNext());
      } else {  // targetSdk 29, nothing from the cursor/uri
          peek.append("uri empty");
      }
  } else {
      peek.append("cursor null");
  }
  </code></pre>
  
  <p>*(<em>note that this is on Emulator Android Q, I don't have a physical Q device)</em></p>
  
  <p>And yes, <code>READ_EXTERNAL_STORAGE</code> and <code>WRITE_EXTERNAL_STORAGE</code> granted, and also tried <code>android:requestLegacyExternalStorage="true"</code></p>
  
  <p>And as expected, the old way to do this also doesn't work:</p>
  
  <pre><code>File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
  </code></pre>
  
  <p><em>(Of course, when I switch back to <code>targetSdk 28</code>, all of the above work again.)</em></p>
  
  <p>Am I missing some other API/method? Thanks!</p>
  
  <p>EDIT: added <code>query</code> code</p>