From 3bbfb0d19722fdac2f827deebc0030ce7a1f7291 Mon Sep 17 00:00:00 2001 From: wolpi Date: Sat, 19 Oct 2024 14:05:48 +0200 Subject: [PATCH] More usage of URI-base-SAF-API (instead of DocumentFile) in SAF to avoid calls to listFiles() which is very slow, but use cursor to list children, see GH issues #372 and #376 --- .../org/primftpd/filesystem/RoSafFile.java | 1 + .../src/org/primftpd/filesystem/SafFile.java | 63 ++++++++++++++++--- .../filesystem/SafFileSystemView.java | 4 ++ 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/primitiveFTPd/src/org/primftpd/filesystem/RoSafFile.java b/primitiveFTPd/src/org/primftpd/filesystem/RoSafFile.java index 4153ccfe..2c70a15b 100644 --- a/primitiveFTPd/src/org/primftpd/filesystem/RoSafFile.java +++ b/primitiveFTPd/src/org/primftpd/filesystem/RoSafFile.java @@ -26,6 +26,7 @@ public abstract class RoSafFile listFiles() { logger.trace("[{}] listFiles()", name); postClientAction(ClientActionEvent.ClientAction.LIST_DIR); - DocumentFile[] children = documentFile.listFiles(); - List result = new ArrayList<>(children.length); - for (DocumentFile child : children) { - String absPath = this.absPath.endsWith("/") - ? this.absPath + child.getName() - : this.absPath + "/" + child.getName(); - result.add(createFile(absPath, documentFile, child)); + // listFiles() is very slow, use URI-based-SAF-API as in RoSAF instead +// DocumentFile[] children = documentFile.listFiles(); +// List result = new ArrayList<>(children.length); +// for (DocumentFile child : children) { +// String absPath = this.absPath.endsWith("/") +// ? this.absPath + child.getName() +// : this.absPath + "/" + child.getName(); +// result.add(createFile(absPath, documentFile, child)); +// } + + List result = new ArrayList<>(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + Uri startUrl = getStartUrl(); + Context context = getPftpdService().getContext(); + + Cursor childCursor = null; + try { + String documentId = DocumentsContract.getDocumentId(documentFile.getUri()); + Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree( + startUrl, + documentId); + childCursor = context.getContentResolver().query( + childrenUri, + RoSafFile.SAF_QUERY_COLUMNS, + null, + null, + null); + while (childCursor.moveToNext()) { + String absPath = this.absPath.endsWith("/") + ? this.absPath + childCursor.getString(RoSafFile.CURSOR_INDEX_NAME) + : this.absPath + "/" + childCursor.getString(RoSafFile.CURSOR_INDEX_NAME); + String childId = childCursor.getString(RoSafFile.CURSOR_INDEX_ID); + Uri childUri = DocumentsContract.buildDocumentUriUsingTree(startUrl, childId); + DocumentFile childDocFile = DocumentFile.fromTreeUri(context, childUri); + result.add(createFile(absPath, documentFile, childDocFile)); + } + } catch (Exception e) { + logger.error("", e); + Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); + } finally { + closeQuietly(childCursor); + } } + logger.trace(" [{}] listFiles(): num children: {}", name, result.size()); return result; } + private void closeQuietly(Cursor cursor) { + if (cursor != null) { + cursor.close(); + } + } + public OutputStream createOutputStream(long offset) throws IOException { logger.trace("[{}] createOutputStream(offset: {})", name, offset); postClientAction(ClientActionEvent.ClientAction.UPLOAD); diff --git a/primitiveFTPd/src/org/primftpd/filesystem/SafFileSystemView.java b/primitiveFTPd/src/org/primftpd/filesystem/SafFileSystemView.java index 7f2717ea..03470673 100644 --- a/primitiveFTPd/src/org/primftpd/filesystem/SafFileSystemView.java +++ b/primitiveFTPd/src/org/primftpd/filesystem/SafFileSystemView.java @@ -30,6 +30,10 @@ public SafFileSystemView(PftpdService pftpdService, Uri startUrl) { this.timeResolution = StorageManagerUtil.getFilesystemTimeResolutionForTreeUri(startUrl); } + public final Uri getStartUrl() { + return startUrl; + } + protected abstract TFile createFile( String absPath, DocumentFile parentDocumentFile,