Skip to content

Commit

Permalink
Merge pull request #17465 from hrydgard/android-content-uri-cpp
Browse files Browse the repository at this point in the history
AndroidContentURI: Move code from the header to cpp.
  • Loading branch information
hrydgard committed May 16, 2023
2 parents c729519 + cf9a628 commit 07a96b8
Show file tree
Hide file tree
Showing 21 changed files with 317 additions and 256 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ add_library(Common STATIC
Common/File/VFS/DirectoryReader.h
Common/File/AndroidStorage.h
Common/File/AndroidStorage.cpp
Common/File/AndroidContentURI.h
Common/File/AndroidContentURI.cpp
Common/File/DiskFree.h
Common/File/DiskFree.cpp
Common/File/Path.h
Expand Down
2 changes: 2 additions & 0 deletions Common/Common.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@
<ClInclude Include="Data\Text\Parsers.h" />
<ClInclude Include="Data\Text\WrapText.h" />
<ClInclude Include="FakeEmitter.h" />
<ClInclude Include="File\AndroidContentURI.h" />
<ClInclude Include="File\AndroidStorage.h" />
<ClInclude Include="File\DirListing.h" />
<ClInclude Include="File\DiskFree.h" />
Expand Down Expand Up @@ -874,6 +875,7 @@
<ClCompile Include="Data\Text\I18n.cpp" />
<ClCompile Include="Data\Text\Parsers.cpp" />
<ClCompile Include="Data\Text\WrapText.cpp" />
<ClCompile Include="File\AndroidContentURI.cpp" />
<ClCompile Include="File\AndroidStorage.cpp" />
<ClCompile Include="File\DirListing.cpp" />
<ClCompile Include="File\DiskFree.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions Common/Common.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,9 @@
<ClInclude Include="System\Request.h">
<Filter>System</Filter>
</ClInclude>
<ClInclude Include="File\AndroidContentURI.h">
<Filter>File</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
Expand Down Expand Up @@ -926,6 +929,9 @@
<ClCompile Include="System\Request.cpp">
<Filter>System</Filter>
</ClCompile>
<ClCompile Include="File\AndroidContentURI.cpp">
<Filter>File</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
Expand Down
182 changes: 182 additions & 0 deletions Common/File/AndroidContentURI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include "Common/File/AndroidContentURI.h"

bool AndroidContentURI::Parse(const std::string &path) {
const char *prefix = "content://";
if (!startsWith(path, prefix)) {
return false;
}

std::string components = path.substr(strlen(prefix));

std::vector<std::string> parts;
SplitString(components, '/', parts);
if (parts.size() == 3) {
// Single file URI.
provider = parts[0];
if (parts[1] == "tree") {
// Single directory URI.
// Not sure when we encounter these?
// file empty signals this type.
root = UriDecode(parts[2]);
return true;
} else if (parts[1] == "document") {
// root empty signals this type.
file = UriDecode(parts[2]);
return true;
} else {
// What's this?
return false;
}
} else if (parts.size() == 5) {
// Tree URI
provider = parts[0];
if (parts[1] != "tree") {
return false;
}
root = UriDecode(parts[2]);
if (parts[3] != "document") {
return false;
}
file = UriDecode(parts[4]);
// Sanity check file path.
return startsWith(file, root);
} else {
// Invalid Content URI
return false;
}
}

AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePath) {
if (root.empty()) {
ERROR_LOG(SYSTEM, "WithRootFilePath cannot be used with single file URIs.");
return *this;
}

AndroidContentURI uri = *this;
uri.file = uri.root;
if (!filePath.empty()) {
uri.file += "/" + filePath;
}
return uri;
}

AndroidContentURI AndroidContentURI::WithComponent(const std::string &filePath) {
AndroidContentURI uri = *this;
uri.file = uri.file + "/" + filePath;
return uri;
}

AndroidContentURI AndroidContentURI::WithExtraExtension(const std::string &extension) {
AndroidContentURI uri = *this;
uri.file = uri.file + extension;
return uri;
}

AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {
_dbg_assert_(!oldExtension.empty() && oldExtension[0] == '.');
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
AndroidContentURI uri = *this;
if (endsWithNoCase(file, oldExtension)) {
uri.file = file.substr(0, file.size() - oldExtension.size()) + newExtension;
}
return uri;
}

AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &newExtension) const {
_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');
AndroidContentURI uri = *this;
if (file.empty()) {
return uri;
}
std::string extension = GetFileExtension();
uri.file = file.substr(0, file.size() - extension.size()) + newExtension;
return uri;
}

bool AndroidContentURI::CanNavigateUp() const {
if (root.empty()) {
return false;
}
return file.size() > root.size();
}

// Only goes downwards in hierarchies. No ".." will ever be generated.
bool AndroidContentURI::ComputePathTo(const AndroidContentURI &other, std::string &path) const {
size_t offset = FilePath().size() + 1;
std::string otherFilePath = other.FilePath();
if (offset >= otherFilePath.size()) {
ERROR_LOG(SYSTEM, "Bad call to PathTo. '%s' -> '%s'", FilePath().c_str(), other.FilePath().c_str());
return false;
}

path = other.FilePath().substr(FilePath().size() + 1);
return true;
}

std::string AndroidContentURI::GetFileExtension() const {
size_t pos = file.rfind(".");
if (pos == std::string::npos) {
return "";
}
size_t slash_pos = file.rfind("/");
if (slash_pos != std::string::npos && slash_pos > pos) {
// Don't want to detect "df/file" from "/as.df/file"
return "";
}
std::string ext = file.substr(pos);
for (size_t i = 0; i < ext.size(); i++) {
ext[i] = tolower(ext[i]);
}
return ext;
}

std::string AndroidContentURI::GetLastPart() const {
if (file.empty()) {
// Can't do anything anyway.
return std::string();
}

if (!CanNavigateUp()) {
size_t colon = file.rfind(':');
if (colon == std::string::npos) {
return std::string();
}
return file.substr(colon + 1);
}

size_t slash = file.rfind('/');
if (slash == std::string::npos) {
return std::string();
}

std::string part = file.substr(slash + 1);
return part;
}

bool AndroidContentURI::NavigateUp() {
if (!CanNavigateUp()) {
return false;
}

size_t slash = file.rfind('/');
if (slash == std::string::npos) {
return false;
}

file = file.substr(0, slash);
return true;
}


std::string AndroidContentURI::ToString() const {
if (file.empty()) {
// Tree URI
return StringFromFormat("content://%s/tree/%s", provider.c_str(), UriEncode(root).c_str());
} else if (root.empty()) {
// Single file URI
return StringFromFormat("content://%s/document/%s", provider.c_str(), UriEncode(file).c_str());
} else {
// File URI from Tree
return StringFromFormat("content://%s/tree/%s/document/%s", provider.c_str(), UriEncode(root).c_str(), UriEncode(file).c_str());
}
}
69 changes: 69 additions & 0 deletions Common/File/AndroidContentURI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include <string>

#include "Common/StringUtils.h"
#include "Common/Net/URL.h"
#include "Common/Log.h"

// Utility to deal with Android storage URIs of the forms:
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO/document/primary%3APSP%20ISO

// This file compiles on all platforms, to reduce the need for ifdefs.

// I am not 100% sure it's OK to rely on the internal format of file content URIs.
// On the other hand, I'm sure tons of apps would break if these changed, so I think we can
// consider them pretty stable. Additionally, the official Document library just manipulates the URIs
// in similar ways...
class AndroidContentURI {
private:
std::string provider;
std::string root;
std::string file;
public:
AndroidContentURI() {}
explicit AndroidContentURI(const std::string &path) {
Parse(path);
}

bool Parse(const std::string &path);

AndroidContentURI WithRootFilePath(const std::string &filePath);
AndroidContentURI WithComponent(const std::string &filePath);
AndroidContentURI WithExtraExtension(const std::string &extension);
AndroidContentURI WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const;
AndroidContentURI WithReplacedExtension(const std::string &newExtension) const;

bool CanNavigateUp() const;

// Only goes downwards in hierarchies. No ".." will ever be generated.
bool ComputePathTo(const AndroidContentURI &other, std::string &path) const;

std::string GetFileExtension() const;
std::string GetLastPart() const;

bool NavigateUp();

bool TreeContains(const AndroidContentURI &fileURI) {
if (root.empty()) {
return false;
}
return startsWith(fileURI.file, root);
}

std::string ToString() const;

// Never store the output of this, only show it to the user.
std::string ToVisualString() const {
return file;
}

const std::string &FilePath() const {
return file;
}

const std::string &RootPath() const {
return root.empty() ? file : root;
}
};
3 changes: 2 additions & 1 deletion Common/File/FileUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@
#include "ppsspp_config.h"

#include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"

#ifdef __MINGW32__
#include <unistd.h>
#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
#endif
#endif

#include <cstring>
#include <ctime>
#include <memory>

#include "Common/Log.h"
#include "Common/LogReporting.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/File/FileUtil.h"
#include "Common/StringUtils.h"
#include "Common/SysError.h"
Expand Down
6 changes: 3 additions & 3 deletions Common/File/Path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
#include <cstring>

#include "Common/File/Path.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/File/FileUtil.h"
#include "Common/StringUtils.h"
#include "Common/Log.h"
#include "Common/Data/Encoding/Utf8.h"

#include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"

#if HOST_IS_CASE_SENSITIVE
#include <dirent.h>
Expand Down Expand Up @@ -157,7 +157,7 @@ std::string Path::GetFilename() const {
return path_;
}

static std::string GetExtFromString(const std::string &str) {
std::string GetExtFromString(const std::string &str) {
size_t pos = str.rfind(".");
if (pos == std::string::npos) {
return "";
Expand All @@ -177,7 +177,7 @@ static std::string GetExtFromString(const std::string &str) {
std::string Path::GetFileExtension() const {
if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_);
return GetExtFromString(uri.FilePath());
return uri.GetFileExtension();
}
return GetExtFromString(path_);
}
Expand Down
2 changes: 2 additions & 0 deletions Common/File/Path.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ class Path {
PathType type_;
};

// Utility function for parsing out file extensions.
std::string GetExtFromString(const std::string &str);

// Utility function for fixing the case of paths. Only present on Unix-like systems.

Expand Down
1 change: 0 additions & 1 deletion Common/File/PathBrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

#if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/app-android.h"
#include "android/jni/AndroidContentURI.h"
#endif

bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *cancel, std::vector<File::FileInfo> &files) {
Expand Down
2 changes: 1 addition & 1 deletion Common/GPU/Vulkan/VulkanQueueRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

using namespace PPSSPP_VK;

// Debug help: adb logcat -s DEBUG PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState
// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper

static void MergeRenderAreaRectInto(VkRect2D *dest, const VkRect2D &src) {
if (dest->offset.x > src.offset.x) {
Expand Down
Loading

0 comments on commit 07a96b8

Please sign in to comment.