From ab69b896c3b2bd383990056ec0bca88424f90555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 15:32:15 +0200 Subject: [PATCH] Add and hook up Android_MoveFile --- Common/File/AndroidStorage.cpp | 19 ++++++++++ Common/File/AndroidStorage.h | 4 ++ Common/File/FileUtil.cpp | 37 ++++++++++++------- UWP/CommonUWP/CommonUWP.vcxproj | 4 +- UWP/CommonUWP/CommonUWP.vcxproj.filters | 8 +++- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 17 ++++++++- 6 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 980bfaa9e2..d7de92c64e 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -12,6 +12,7 @@ static jmethodID listContentUriDir; static jmethodID contentUriCreateFile; static jmethodID contentUriCreateDirectory; static jmethodID contentUriCopyFile; +static jmethodID contentUriMoveFile; static jmethodID contentUriRemoveFile; static jmethodID contentUriRenameFileTo; static jmethodID contentUriGetFileInfo; @@ -39,6 +40,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriCopyFile); contentUriRemoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriRemoveFile", "(Ljava/lang/String;)Z"); _dbg_assert_(contentUriRemoveFile); + contentUriMoveFile = env->GetMethodID(env->GetObjectClass(obj), "contentUriMoveFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + _dbg_assert_(contentUriMoveFile); contentUriRenameFileTo = env->GetMethodID(env->GetObjectClass(obj), "contentUriRenameFileTo", "(Ljava/lang/String;Ljava/lang/String;)Z"); _dbg_assert_(contentUriRenameFileTo); contentUriGetFileInfo = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFileInfo", "(Ljava/lang/String;)Ljava/lang/String;"); @@ -111,6 +114,17 @@ bool Android_CopyFile(const std::string &fileUri, const std::string &destParentU return env->CallBooleanMethod(g_nativeActivity, contentUriCopyFile, paramFileName, paramDestParentUri); } +bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { + if (!g_nativeActivity) { + return false; + } + auto env = getEnv(); + jstring paramFileName = env->NewStringUTF(fileUri.c_str()); + jstring paramSrcParentUri = env->NewStringUTF(srcParentUri.c_str()); + jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); + return env->CallBooleanMethod(g_nativeActivity, contentUriMoveFile, paramFileName, paramSrcParentUri, paramDestParentUri); +} + bool Android_RemoveFile(const std::string &fileUri) { if (!g_nativeActivity) { return false; @@ -262,4 +276,9 @@ const char *Android_ErrorToString(ContentError error) { } } +#else + +// This string should never appear except on Android. +std::string g_extFilesDir = "(IF YOU SEE THIS THERE'S A BUG)"; + #endif diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 55d49da478..9decabaf48 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -41,6 +41,7 @@ bool Android_IsContentUri(const std::string &uri); int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode); bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName); bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName); +bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri); bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri); bool Android_RemoveFile(const std::string &fileUri); bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName); @@ -57,12 +58,15 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj); #else +extern std::string g_extFilesDir; + // Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere. inline bool Android_IsContentUri(const std::string &uri) { return false; } inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; } inline bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return false; } inline bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return false; } +inline bool Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return false; } inline bool Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) { return false; } inline bool Android_RemoveFile(const std::string &fileUri) { return false; } inline bool Android_RenameFileTo(const std::string &fileUri, const std::string &newName) { return false; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index f43e443c21..8560694a48 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -571,6 +571,7 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { break; case PathType::CONTENT_URI: // Content URI: Can only rename if in the same folder. + // TODO: Fallback to move + rename? Or do we even care about that use case? if (srcFilename.GetDirectory() != destFilename.GetDirectory()) { INFO_LOG(COMMON, "Content URI rename: Directories not matching, failing. %s --> %s", srcFilename.c_str(), destFilename.c_str()); return false; @@ -600,26 +601,23 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { } // copies file srcFilename to destFilename, returns true on success -bool Copy(const Path &srcFilename, const Path &destFilename) -{ +bool Copy(const Path &srcFilename, const Path &destFilename) { switch (srcFilename.Type()) { case PathType::NATIVE: break; // OK case PathType::CONTENT_URI: - ERROR_LOG_REPORT_ONCE(copyUriNotSupported, COMMON, "Copying files by Android URI is not yet supported"); + if (destFilename.Type() == PathType::CONTENT_URI && destFilename.CanNavigateUp()) { + Path destParent = destFilename.NavigateUp(); + // Use native file copy. + if (Android_CopyFile(srcFilename.ToString(), destParent.ToString())) { + return true; + } + // Else fall through, and try using file I/O. + } break; default: return false; } - switch (destFilename.Type()) { - case PathType::NATIVE: - break; // OK - case PathType::CONTENT_URI: - ERROR_LOG_REPORT_ONCE(copyUriNotSupported, COMMON, "Copying files by Android URI is not yet supported"); - return false; - default: - return false; - } INFO_LOG(COMMON, "Copy: %s --> %s", srcFilename.c_str(), destFilename.c_str()); #ifdef _WIN32 @@ -637,7 +635,7 @@ bool Copy(const Path &srcFilename, const Path &destFilename) #else // buffer size -#define BSIZE 4096 +#define BSIZE 16384 char buffer[BSIZE]; @@ -692,6 +690,19 @@ bool Copy(const Path &srcFilename, const Path &destFilename) } bool Move(const Path &srcFilename, const Path &destFilename) { + // Try a shortcut in Android Storage scenarios. + if (srcFilename.Type() == PathType::CONTENT_URI && destFilename.Type() == PathType::CONTENT_URI && srcFilename.CanNavigateUp() && destFilename.CanNavigateUp()) { + // We do not handle simultaneous renames here. + if (srcFilename.GetFilename() == destFilename.GetFilename()) { + Path srcParent = srcFilename.NavigateUp(); + Path dstParent = destFilename.NavigateUp(); + if (Android_MoveFile(srcFilename.ToString(), srcParent.ToString(), dstParent.ToString())) { + return true; + } + // If failed, fall through and try other ways. + } + } + if (Rename(srcFilename, destFilename)) { return true; } else if (Copy(srcFilename, destFilename)) { diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index e91ec0d58b..3349afd9e6 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -386,6 +386,7 @@ + @@ -518,6 +519,7 @@ + @@ -640,4 +642,4 @@ - + \ No newline at end of file diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index a72807393e..740747dfd4 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -375,6 +375,9 @@ GPU + + File + @@ -691,6 +694,9 @@ GPU + + File + @@ -705,4 +711,4 @@ Math\lin - + \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 02c733ec5c..89e02699c9 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -268,14 +268,27 @@ public class PpssppActivity extends NativeActivity { try { Uri srcUri = Uri.parse(srcFileUri); Uri dstParentUri = Uri.parse(dstParentDirUri); - DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri); - return true; + return DocumentsContract.copyDocument(getContentResolver(), srcUri, dstParentUri) != null; } catch (Exception e) { Log.e(TAG, "contentUriCopyFile exception: " + e.toString()); return false; } } + // NOTE: The destination is the parent directory! This means that contentUriCopyFile + // cannot rename things as part of the operation. + public boolean contentUriMoveFile(String srcFileUri, String srcParentDirUri, String dstParentDirUri) { + try { + Uri srcUri = Uri.parse(srcFileUri); + Uri srcParentUri = Uri.parse(srcParentDirUri); + Uri dstParentUri = Uri.parse(dstParentDirUri); + return DocumentsContract.moveDocument(getContentResolver(), srcUri, srcParentUri, dstParentUri) != null; + } catch (Exception e) { + Log.e(TAG, "contentUriMoveFile exception: " + e.toString()); + return false; + } + } + public boolean contentUriRenameFileTo(String fileUri, String newName) { try { Uri uri = Uri.parse(fileUri);