diff --git a/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java b/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java index 528e23d2a..16b9b175f 100644 --- a/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java +++ b/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java @@ -1,13 +1,20 @@ package com.capacitorjs.plugins.filesystem; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; import android.net.Uri; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.provider.DocumentsContract; import android.util.Base64; import com.capacitorjs.plugins.filesystem.exceptions.CopyFailedException; import com.capacitorjs.plugins.filesystem.exceptions.DirectoryExistsException; import com.capacitorjs.plugins.filesystem.exceptions.DirectoryNotFoundException; import com.getcapacitor.Bridge; +import com.getcapacitor.JSArray; import com.getcapacitor.JSObject; import com.getcapacitor.PluginCall; import com.getcapacitor.plugin.util.CapacitorHttpUrlConnection; @@ -100,6 +107,61 @@ public File[] readdir(String path, String directory) throws DirectoryNotFoundExc return files; } + public void readdir_content_uri(final PluginCall call, final Uri uri, boolean callback) { + ContentResolver resolver = context.getContentResolver(); + + Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getDocumentId(uri)); + String[] projections = { + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_MIME_TYPE, + DocumentsContract.Document.COLUMN_SIZE, + DocumentsContract.Document.COLUMN_LAST_MODIFIED, + DocumentsContract.Document.COLUMN_DOCUMENT_ID + }; + Cursor c = resolver.query(childrenUri, projections, null, null); + + if (c == null) { + call.reject("Document does not exist"); + return; + } + + Looper looper = Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + + ContentObserver observer = new ContentObserver(new Handler(looper)) { + @Override + public void onChange(boolean selfChange) { + if (!selfChange) { + c.unregisterContentObserver(this); + } + + JSArray filesArray = new JSArray(); + while (c.moveToNext()) { + JSObject data = new JSObject(); + data.put("name", c.getString(0)); + data.put("type", DocumentsContract.Document.MIME_TYPE_DIR.equals(c.getString(1)) ? "directory" : "file"); + data.put("size", c.getLong(2)); + data.put("mtime", c.getLong(3)); + data.put("ctime", null); + data.put("uri", DocumentsContract.buildDocumentUriUsingTree(uri, c.getString(4))); + filesArray.put(data); + } + + JSObject ret = new JSObject(); + ret.put("files", filesArray); + call.resolve(ret); + } + }; + + if (!callback && c.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING, false)) { + c.registerContentObserver(observer); + } else { + observer.onChange(true); + } + } + public File copy(String from, String directory, String to, String toDirectory, boolean doRename) throws IOException, CopyFailedException { if (toDirectory == null) { @@ -222,6 +284,7 @@ public File getFileObject(String path, String directory) { if (u.getScheme() == null || u.getScheme().equals("file")) { return new File(u.getPath()); } + return null; } File androidDirectory = this.getDirectory(directory); diff --git a/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/FilesystemPlugin.java b/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/FilesystemPlugin.java index 99bff1fb5..e4d19a5f6 100644 --- a/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/FilesystemPlugin.java +++ b/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/FilesystemPlugin.java @@ -1,10 +1,12 @@ package com.capacitorjs.plugins.filesystem; import android.Manifest; +import android.database.Cursor; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Environment; +import android.provider.DocumentsContract.Document; import com.capacitorjs.plugins.filesystem.exceptions.CopyFailedException; import com.capacitorjs.plugins.filesystem.exceptions.DirectoryExistsException; import com.capacitorjs.plugins.filesystem.exceptions.DirectoryNotFoundException; @@ -270,7 +272,10 @@ public void readdir(PluginCall call) { String path = call.getString("path"); String directory = getDirectoryParameter(call); - if (isPublicDirectory(directory) && !isStoragePermissionGranted()) { + if (directory == null && isContentURL(path)) { + Uri uri = Uri.parse(path); + implementation.readdir_content_uri(call, uri, false); + } else if (isPublicDirectory(directory) && !isStoragePermissionGranted()) { requestAllPermissions(call, "permissionCallback"); } else { try { @@ -336,11 +341,31 @@ public void stat(PluginCall call) { String path = call.getString("path"); String directory = getDirectoryParameter(call); - File fileObject = implementation.getFileObject(path, directory); + if (directory == null && isContentURL(path)) { + String[] projection = {Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE, Document.COLUMN_LAST_MODIFIED}; + Cursor c = getContext().getContentResolver().query(Uri.parse(path), projection, null, null); - if (isPublicDirectory(directory) && !isStoragePermissionGranted()) { + if (c == null) { + call.reject("Document does not exist"); + return; + } else if (c.getCount() != 1) { + call.reject("Provider returned unexpected data (file might not exist)"); + return; + } + + JSObject data = new JSObject(); + c.moveToNext(); + data.put("type", Document.MIME_TYPE_DIR.equals(c.getString(0)) ? "directory" : "file"); + data.put("size", c.getLong(1)); + data.put("mtime", c.getLong(2)); + data.put("ctime", null); + data.put("uri", path); + + call.resolve(data); + } else if (isPublicDirectory(directory) && !isStoragePermissionGranted()) { requestAllPermissions(call, "permissionCallback"); } else { + File fileObject = implementation.getFileObject(path, directory); if (!fileObject.exists()) { call.reject("File does not exist"); return; @@ -510,6 +535,11 @@ private void permissionCallback(PluginCall call) { } } + private boolean isContentURL(String path) { + Uri u = Uri.parse(path); + return "content".equals(u.getScheme()); + } + /** * Checks the the given permission is granted or not * @return Returns true if the app is running on Android 30 or newer or if the permission is already granted