I have an issue for months, need to show all list file path (or Uri) of file ".epub" on my phone (SD-card, memory card). I use this to get root Uri (user pick folder):
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
resultUri = data.getData();
The result uri will looks like: content://com.android.externalstorage.documents/tree/primary%3ABooks
I used a recursive function like this to fetch that Uri, but the function run very slow for result:
DocumentFile F0 = DocumentFile.fromTreeUri(context, inputUri);
if (F0 != null) {
DocumentFile[] F1 = F0.listFiles();
for (DocumentFile F2 : F1) {
recuisiveUri(F2);
}
}
private void recuisiveUri(DocumentFile F2) {
Log.d(TAG, "recuisiveUri: F2 name = " + F2.getName()) ;
if(F2.canRead()
&& F2.exists()
&& F2.canWrite()
&& F2.canRead()
&& !F2.getName().contains("Android")
&& !F2.getName().contains("data")
&& !F2.getName().contains("DCIM")
){
if (F2.isDirectory()) {
DocumentFile[] F3 = F2.listFiles();
for (DocumentFile F4 : F3) {
recuisiveUri(F4);
}
} else {
if (Objects.requireNonNull(F2.getName()).endsWith(".epub"))
listUri.add(F2.getUri());
}
}
}
Anyone if you have better solution (faster) or found something wrong in my code, please help me, thank very a lot!
The problem is DocumentFile it is even specified in documentation that it is very slow:
It offers a simplified view of a tree of documents, but it has substantial overhead. For optimal performance and a richer feature set, use the DocumentsContract methods and constants directly.
also read this blog post: Scoped Storage Stories: DocumentsContract
As this blog entry and android documentation (above) suggests you should work directly with DocumentsContract. Actually what you should do is to write your own listFiles but optimized - see answer here https://stackoverflow.com/a/66296597/471160
[edit]
Below is a code which minimizes use of ContentProvider to list recursively file tree (but it does not use recursion - but a stack collection).
In your code replace:
DocumentFile[] F1 = F0.listFiles();
for (DocumentFile F2 : F1) {
recuisiveUri(F2);
}
with:
listUri = traverseDirectoryEntries(resultUri);
Code is as follows (its based on Issues traversing through directory hierarchy with Android Storage Access Framework / DocumentProvider using MTP ). I have not tested it on huge folder hierarchies - only on emulator. I am not sure if it properly filters for ".epub" - I think you should be able to handle it.
ArrayList<Uri> traverseDirectoryEntries(Uri rootUri) {
ArrayList<Uri> listUri = new ArrayList<>();
ContentResolver contentResolver = this.getContentResolver();
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri,
DocumentsContract.getTreeDocumentId(rootUri));
// Keep track of our directory hierarchy
List<Uri> dirNodes = new LinkedList<>();
dirNodes.add(childrenUri);
while(!dirNodes.isEmpty()) {
childrenUri = dirNodes.remove(0); // get the item from top
Log.d(LOG_TAG, "node uri: " + childrenUri);
Cursor c = contentResolver.query(childrenUri, new String[]{
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE},
null, null, null);
try {
while (c.moveToNext()) {
final String docId = c.getString(0);
final String name = c.getString(1);
final String mime = c.getString(2);
Log.d(LOG_TAG, "docId: " + docId + ", name: " + name + ", mime: " + mime);
if(isDirectory(mime)) {
final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
dirNodes.add(newNode);
}
else {
if (name.contains(".epub")) { // maybe you should check mime here?
final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
listUri.add(newNode);
}
}
}
} finally {
closeQuietly(c);
}
}
return listUri;
}
// Util method to check if the mime type is a directory
private static boolean isDirectory(String mimeType) {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
// Util method to close a closeable
private static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (RuntimeException re) {
throw re;
} catch (Exception ignore) {
// ignore exception
}
}
}
I think your problem is fairly simple provided you are comfortable with Kotlin.
Here's how you can get all.epub files in a directory:
val rootDir = File(uri.path) // uri pointing towards the chosen directory
val allEpubFiles = root.list { dir, name -> name.endsWith(".epub") }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.