android: Fix resolving android URIs in native code
This commit is contained in:
parent
a9e29a3972
commit
585b6e9d46
7 changed files with 117 additions and 11 deletions
|
@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
|
@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
|
@ -68,7 +69,7 @@ object NativeLibrary {
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(path: String?, openmode: String?): Int {
|
fun openContentUri(path: String?, openmode: String?): Int {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.openContentUri(path, openmode)
|
FileUtil.openContentUri(path, openmode)
|
||||||
|
@ -78,7 +79,7 @@ object NativeLibrary {
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSize(path: String?): Long {
|
fun getSize(path: String?): Long {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(path)
|
FileUtil.getFileSize(path)
|
||||||
|
@ -88,7 +89,7 @@ object NativeLibrary {
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun exists(path: String?): Boolean {
|
fun exists(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.exists(path)
|
YuzuApplication.documentsTree!!.exists(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.exists(path)
|
FileUtil.exists(path)
|
||||||
|
@ -98,13 +99,31 @@ object NativeLibrary {
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isDirectory(path: String?): Boolean {
|
fun isDirectory(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.isDirectory(path)
|
FileUtil.isDirectory(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getParentDirectory(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getParentDirectory(path)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getFilename(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getFilename(path)
|
||||||
|
} else {
|
||||||
|
FileUtil.getFilename(Uri.parse(path))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if pro controller isn't available and handheld is
|
* Returns true if pro controller isn't available and handheld is
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,6 +42,23 @@ class DocumentsTree {
|
||||||
return node != null && node.isDirectory
|
return node != null && node.isDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getParentDirectory(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)!!
|
||||||
|
val parentNode = node.parent
|
||||||
|
if (parentNode != null && parentNode.isDirectory) {
|
||||||
|
return parentNode.uri!!.toString()
|
||||||
|
}
|
||||||
|
return node.uri!!.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilename(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)
|
||||||
|
if (node != null) {
|
||||||
|
return node.name!!
|
||||||
|
}
|
||||||
|
return filepath
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolvePath(filepath: String): DocumentsNode? {
|
private fun resolvePath(filepath: String): DocumentsNode? {
|
||||||
val tokens = StringTokenizer(filepath, File.separator, false)
|
val tokens = StringTokenizer(filepath, File.separator, false)
|
||||||
var iterator = root
|
var iterator = root
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/perf_stats.h"
|
|
||||||
#include "jni/emu_window/emu_window.h"
|
|
||||||
#include "jni/applets/software_keyboard.h"
|
|
||||||
#include "video_core/rasterizer_interface.h"
|
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
#include "jni/applets/software_keyboard.h"
|
||||||
|
#include "jni/emu_window/emu_window.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/fs/fs_android.h"
|
#include "common/fs/fs_android.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
|
@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) {
|
||||||
env->GetJavaVM(&g_jvm);
|
env->GetJavaVM(&g_jvm);
|
||||||
native_library = clazz;
|
native_library = clazz;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define F(JMethodID, JMethodName, Signature) \
|
#define F(JMethodID, JMethodName, Signature) \
|
||||||
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnRegisterCallbacks() {
|
void UnRegisterCallbacks() {
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) JMethodID = nullptr;
|
#define F(JMethodID) JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsContentUri(const std::string& path) {
|
bool IsContentUri(const std::string& path) {
|
||||||
|
@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(FunctionName, JMethodID, Caller)
|
||||||
|
#define F(FunctionName, JMethodID, Caller) \
|
||||||
|
std::string FunctionName(const std::string& filepath) { \
|
||||||
|
if (JMethodID == nullptr) { \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
auto env = GetEnvForThread(); \
|
||||||
|
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
|
||||||
|
jstring j_return = \
|
||||||
|
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
|
||||||
|
if (!j_return) { \
|
||||||
|
return {}; \
|
||||||
|
} \
|
||||||
|
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
|
||||||
|
const jsize length = env->GetStringLength(j_return); \
|
||||||
|
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
|
||||||
|
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
|
||||||
|
env->ReleaseStringChars(j_return, jchars); \
|
||||||
|
return converted_string; \
|
||||||
|
}
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
|
|
@ -17,19 +17,28 @@
|
||||||
"(Ljava/lang/String;)Z") \
|
"(Ljava/lang/String;)Z") \
|
||||||
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
||||||
|
|
||||||
|
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
|
||||||
|
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;") \
|
||||||
|
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;")
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
static JavaVM* g_jvm = nullptr;
|
static JavaVM* g_jvm = nullptr;
|
||||||
static jclass native_library = nullptr;
|
static jclass native_library = nullptr;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
|
|
||||||
enum class OpenMode {
|
enum class OpenMode {
|
||||||
Read,
|
Read,
|
||||||
|
@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
|
||||||
|
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
|
|
@ -401,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetParentPath(std::string_view path) {
|
std::string_view GetParentPath(std::string_view path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (path[0] != '/') {
|
||||||
|
std::string path_string{path};
|
||||||
|
return FS::Android::GetParentDirectory(path_string);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const auto name_bck_index = path.rfind('\\');
|
const auto name_bck_index = path.rfind('\\');
|
||||||
const auto name_fwd_index = path.rfind('/');
|
const auto name_fwd_index = path.rfind('/');
|
||||||
std::size_t name_index;
|
std::size_t name_index;
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <common/fs/fs_android.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
/// Make a string lowercase
|
||||||
|
@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (full_path[0] != '/') {
|
||||||
|
*_pPath = Common::FS::Android::GetParentDirectory(full_path);
|
||||||
|
*_pFilename = Common::FS::Android::GetFilename(full_path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::size_t dir_end = full_path.find_last_of("/"
|
std::size_t dir_end = full_path.find_last_of("/"
|
||||||
// windows needs the : included for something like just "C:" to be considered a directory
|
// windows needs the : included for something like just "C:" to be considered a directory
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
Loading…
Reference in a new issue