diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 277498ab50..73de8b7b27 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -114,10 +114,11 @@ bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName return env->CallBooleanMethod(g_nativeActivity, contentUriRenameFileTo, paramFileUri, paramNewName); } +// NOTE: Does not set fullName - you're supposed to already know it. static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) { std::vector parts; SplitString(line, '|', parts); - if (parts.size() != 5) { + if (parts.size() != 4) { ERROR_LOG(FILESYS, "Bad format: %s", line.c_str()); return false; } @@ -125,7 +126,6 @@ static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) { fileInfo->isDirectory = parts[0][0] == 'D'; fileInfo->exists = true; sscanf(parts[1].c_str(), "%" PRIu64, &fileInfo->size); - fileInfo->fullName = Path(parts[3]); fileInfo->isWritable = true; // TODO: Should be passed as part of the string. fileInfo->access = fileInfo->isDirectory ? 0666 : 0777; // TODO: For read-only mappings, reflect that here, similarly as with isWritable. @@ -155,6 +155,8 @@ bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *fileInfo) { } const char *charArray = env->GetStringUTFChars(str, 0); bool retval = ParseFileInfo(std::string(charArray), fileInfo); + fileInfo->fullName = Path(fileUri); + env->DeleteLocalRef(str); return retval && fileInfo->exists; } @@ -189,6 +191,8 @@ std::vector Android_ListContentUri(const std::string &path) { if (charArray) { // paranoia File::FileInfo info; if (ParseFileInfo(std::string(charArray), &info)) { + // We can just reconstruct the URI. + info.fullName = Path(path) / info.name; items.push_back(info); } } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 7d7ad42489..d80a05cd38 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -9,6 +9,8 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.util.Log; import android.os.storage.StorageManager; +import android.content.ContentResolver; +import android.database.Cursor; import android.provider.DocumentsContract; import androidx.documentfile.provider.DocumentFile; import java.util.ArrayList; @@ -136,6 +138,7 @@ public class PpssppActivity extends NativeActivity { } private static String fileInfoToString(DocumentFile file) { + // TODO: Replace with a DocumentsContract query. String str = "F|"; if (file.isVirtual()) { // This we don't want to see. @@ -144,7 +147,7 @@ public class PpssppActivity extends NativeActivity { } else if (file.isDirectory()) { str = "D|"; } - str += file.length() + "|" + file.getName() + "|" + file.getUri() + "|" + file.lastModified(); + str += file.length() + "|" + file.getName() + "|" + file.lastModified(); return str; } @@ -154,13 +157,46 @@ public class PpssppActivity extends NativeActivity { public String[] listContentUriDir(String uriString) { try { Uri uri = Uri.parse(uriString); - DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri); - DocumentFile[] children = documentFile.listFiles(); - ArrayList listing = new ArrayList(); - // Encode entries into strings for JNI simplicity. - for (DocumentFile file : children) { - String str = fileInfoToString(file); - listing.add(str); + + final ContentResolver resolver = getContentResolver(); + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, + DocumentsContract.getDocumentId(uri)); + final ArrayList listing = new ArrayList<>(); + Cursor c = null; + try { + c = resolver.query(childrenUri, new String[] { + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_SIZE, + DocumentsContract.Document.COLUMN_FLAGS, + DocumentsContract.Document.COLUMN_MIME_TYPE, // check for MIME_TYPE_DIR + DocumentsContract.Document.COLUMN_LAST_MODIFIED + }, null, null, null); + while (c.moveToNext()) { + final String documentName = c.getString(0); + final long size = c.getLong(1); + final int flags = c.getInt(2); + final String mimeType = c.getString(3); + final long lastModified = c.getLong(4); + + final boolean isDirectory = mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR); + + // Filter out any nonsense. + if ((flags & (DocumentsContract.Document.FLAG_PARTIAL | DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT)) != 0) { + continue; + } + + String str = "F|"; + if (isDirectory) { + str = "D|"; + } + str += size + "|" + documentName + "|" + lastModified; + + listing.add(str); + } + } catch (Exception e) { + Log.w(TAG, "Failed query: " + e); + } finally { + c.close(); } // Is ArrayList weird or what? String[] strings = new String[listing.size()];