diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java index dbeb410079..d3fbcce9c0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java @@ -74,6 +74,31 @@ public class ContentHandler return false; } + /** + * @return -1 if not found, -2 if directory, file size otherwise + */ + @Keep + public static long getSizeAndIsDirectory(String uri) + { + final String[] projection = new String[]{Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE}; + try (Cursor cursor = getContentResolver().query(Uri.parse(uri), projection, null, null, null)) + { + if (cursor != null && cursor.moveToFirst()) + { + if (Document.MIME_TYPE_DIR.equals(cursor.getString(0))) + return -2; + else + return cursor.isNull(1) ? 0 : cursor.getLong(1); + } + } + catch (SecurityException e) + { + Log.error("Tried to get metadata for " + uri + " without permission"); + } + + return -1; + } + @Nullable public static String getDisplayName(@NonNull Uri uri) { diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp index 2870044ac9..066b8edf27 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp @@ -104,6 +104,14 @@ bool DeleteAndroidContent(const std::string& uri) IDCache::GetContentHandlerDelete(), ToJString(env, uri)); } +jlong GetAndroidContentSizeAndIsDirectory(const std::string& uri) +{ + JNIEnv* env = IDCache::GetEnvForThread(); + return env->CallStaticLongMethod(IDCache::GetContentHandlerClass(), + IDCache::GetContentHandlerGetSizeAndIsDirectory(), + ToJString(env, uri)); +} + int GetNetworkIpAddress() { JNIEnv* env = IDCache::GetEnvForThread(); diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.h b/Source/Android/jni/AndroidCommon/AndroidCommon.h index a196108cfe..5502363c9c 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.h +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.h @@ -25,6 +25,9 @@ int OpenAndroidContent(const std::string& uri, const std::string& mode); // Deletes a given file. bool DeleteAndroidContent(const std::string& uri); +// Returns -1 if not found, -2 if directory, file size otherwise. +jlong GetAndroidContentSizeAndIsDirectory(const std::string& uri); + int GetNetworkIpAddress(); int GetNetworkPrefixLength(); int GetNetworkGateway(); diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 674233d79f..5f5503eb87 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -44,6 +44,7 @@ static jmethodID s_compress_cb_run; static jclass s_content_handler_class; static jmethodID s_content_handler_open_fd; static jmethodID s_content_handler_delete; +static jmethodID s_content_handler_get_size_and_is_directory; static jclass s_network_helper_class; static jmethodID s_network_helper_get_network_ip_address; @@ -210,6 +211,11 @@ jmethodID GetContentHandlerDelete() return s_content_handler_delete; } +jmethodID GetContentHandlerGetSizeAndIsDirectory() +{ + return s_content_handler_get_size_and_is_directory; +} + jclass GetNetworkHelperClass() { return s_network_helper_class; @@ -229,6 +235,7 @@ jmethodID GetNetworkHelperGetNetworkGateway() { return s_network_helper_get_network_gateway; } + } // namespace IDCache #ifdef __cplusplus @@ -306,6 +313,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) "(Ljava/lang/String;Ljava/lang/String;)I"); s_content_handler_delete = env->GetStaticMethodID(s_content_handler_class, "delete", "(Ljava/lang/String;)Z"); + s_content_handler_get_size_and_is_directory = env->GetStaticMethodID( + s_content_handler_class, "getSizeAndIsDirectory", "(Ljava/lang/String;)J"); const jclass network_helper_class = env->FindClass("org/dolphinemu/dolphinemu/utils/NetworkHelper"); diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index c89e0c9fcc..2a6c9ccbb8 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -44,6 +44,7 @@ jmethodID GetCompressCallbackRun(); jclass GetContentHandlerClass(); jmethodID GetContentHandlerOpenFd(); jmethodID GetContentHandlerDelete(); +jmethodID GetContentHandlerGetSizeAndIsDirectory(); jclass GetNetworkHelperClass(); jmethodID GetNetworkHelperGetNetworkIpAddress(); diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 0d41395209..450eeea0a5 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -78,19 +78,40 @@ FileInfo::FileInfo(const char* path) : FileInfo(std::string(path)) #else FileInfo::FileInfo(const std::string& path) : FileInfo(path.c_str()) { +#ifdef ANDROID + if (IsPathAndroidContent(path)) + AndroidContentInit(path); + else +#endif + m_exists = stat(path.c_str(), &m_stat) == 0; } FileInfo::FileInfo(const char* path) { - m_exists = stat(path, &m_stat) == 0; +#ifdef ANDROID + if (IsPathAndroidContent(path)) + AndroidContentInit(path); + else +#endif + m_exists = stat(path, &m_stat) == 0; } #endif FileInfo::FileInfo(int fd) { - m_exists = fstat(fd, &m_stat); + m_exists = fstat(fd, &m_stat) == 0; } +#ifdef ANDROID +void FileInfo::AndroidContentInit(const std::string& path) +{ + const jlong result = GetAndroidContentSizeAndIsDirectory(path); + m_exists = result != -1; + m_stat.st_mode = result == -2 ? S_IFDIR : S_IFREG; + m_stat.st_size = result >= 0 ? result : 0; +} +#endif + bool FileInfo::Exists() const { return m_exists; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 73ba41d2da..0d57e6ba9b 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -113,6 +113,10 @@ public: u64 GetSize() const; private: +#ifdef ANDROID + void AndroidContentInit(const std::string& path); +#endif + struct stat m_stat; bool m_exists; };