diff --git a/Common/File/AndroidContentURI.cpp b/Common/File/AndroidContentURI.cpp index 65526285ecf8..6456b8cad209 100644 --- a/Common/File/AndroidContentURI.cpp +++ b/Common/File/AndroidContentURI.cpp @@ -62,7 +62,16 @@ AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePat AndroidContentURI AndroidContentURI::WithComponent(const std::string &filePath) { AndroidContentURI uri = *this; - uri.file = uri.file + "/" + filePath; + if (uri.file.empty()) { + // Not sure what to do. + return uri; + } + if (uri.file.back() == ':') { + // Special case handling for Document URIs: Treat the ':' as a directory separator too (but preserved in the filename). + uri.file = uri.file + filePath; + } else { + uri.file = uri.file + "/" + filePath; + } return uri; } @@ -94,10 +103,11 @@ AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &ne } bool AndroidContentURI::CanNavigateUp() const { - if (root.empty()) { - return false; + if (IsTreeURI()) { + return file.size() > root.size(); + } else { + return file.find(':') != std::string::npos && file.back() != ':'; } - return file.size() > root.size(); } // Only goes downwards in hierarchies. No ".." will ever be generated. @@ -141,12 +151,20 @@ std::string AndroidContentURI::GetLastPart() const { if (colon == std::string::npos) { return std::string(); } + if (file.back() == ':') { + return file; + } return file.substr(colon + 1); } size_t slash = file.rfind('/'); if (slash == std::string::npos) { - return std::string(); + // ok, look for the final colon. If it's the last char, we would have been caught above in !CanNavigateUp. + size_t colon = file.rfind(':'); + if (colon == std::string::npos) { + return std::string(); + } + return file.substr(colon + 1); } std::string part = file.substr(slash + 1); @@ -160,7 +178,13 @@ bool AndroidContentURI::NavigateUp() { size_t slash = file.rfind('/'); if (slash == std::string::npos) { - return false; + // ok, look for the final colon. + size_t colon = file.rfind(':'); + if (colon == std::string::npos) { + return false; + } + file = file.substr(0, colon + 1); // Note: we include the colon in these paths. + return true; } file = file.substr(0, slash); diff --git a/Common/File/AndroidContentURI.h b/Common/File/AndroidContentURI.h index 3020b4147654..23ce2a9e03ce 100644 --- a/Common/File/AndroidContentURI.h +++ b/Common/File/AndroidContentURI.h @@ -66,4 +66,8 @@ class AndroidContentURI { const std::string &RootPath() const { return root.empty() ? file : root; } + + bool IsTreeURI() const { + return !root.empty(); + } }; diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 2aa53e852608..88de9eab71ba 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -722,14 +722,20 @@ static bool TestAndroidContentURI() { EXPECT_EQ_STR(diff, std::string("Tekken 6.iso")); EXPECT_EQ_STR(fileURI.GetFileExtension(), std::string(".prx")); - EXPECT_FALSE(fileURI.CanNavigateUp()); + EXPECT_TRUE(fileURI.CanNavigateUp()); // Can now virtually navigate up one step from these. - // These are annoying because they hide the actual filename, and we can't get at a parent folder, - // which confuses our elf loading. + // These are annoying because they hide the actual filename, and we can't get at a parent folder. + // Decided to handle the ':' as a directory separator for navigation purposes, which fixes the problem (though not the extension thing). AndroidContentURI downloadURI; EXPECT_TRUE(downloadURI.Parse(std::string(downloadURIString))); EXPECT_EQ_STR(downloadURI.GetLastPart(), std::string("10000000006")); - EXPECT_FALSE(downloadURI.CanNavigateUp()); + EXPECT_TRUE(downloadURI.CanNavigateUp()); + EXPECT_TRUE(downloadURI.NavigateUp()); + // While this is not an openable valid content URI, we can still get something that we can concatenate a filename on top of. + EXPECT_EQ_STR(downloadURI.ToString(), std::string("content://com.android.providers.downloads.documents/document/msf%3A")); + EXPECT_EQ_STR(downloadURI.GetLastPart(), std::string("msf:")); + downloadURI = downloadURI.WithComponent("myfile"); + EXPECT_EQ_STR(downloadURI.ToString(), std::string("content://com.android.providers.downloads.documents/document/msf%3Amyfile")); return true; }