mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-12-16 12:08:49 +00:00
WIP re-implementation of Android Rename using native filesystem manipulation
This commit is contained in:
parent
cb66169c59
commit
c13a122527
@ -7,10 +7,12 @@ package org.citra.citra_emu
|
|||||||
import android.Manifest.permission
|
import android.Manifest.permission
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
@ -18,7 +20,9 @@ import android.view.View
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.citra.citra_emu.activities.EmulationActivity
|
import org.citra.citra_emu.activities.EmulationActivity
|
||||||
import org.citra.citra_emu.utils.FileUtil
|
import org.citra.citra_emu.utils.FileUtil
|
||||||
@ -629,6 +633,23 @@ object NativeLibrary {
|
|||||||
FileUtil.getFilesName(path)
|
FileUtil.getFilesName(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getUserDirectory(): String {
|
||||||
|
val preferences: SharedPreferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||||
|
|
||||||
|
val udUri = preferences.getString("CITRA_DIRECTORY", "")!!.toUri()
|
||||||
|
val udPathSegment = udUri.lastPathSegment!!
|
||||||
|
val udVirtualPath = udPathSegment.removePrefix("primary:")
|
||||||
|
if (udVirtualPath == udPathSegment) {
|
||||||
|
throw IllegalStateException("TODO: User directory must be in primary external storage, is instead: $udVirtualPath")
|
||||||
|
}
|
||||||
|
val userDirNativePath = Environment.getExternalStorageDirectory().absolutePath + "/" + udVirtualPath + "/"
|
||||||
|
|
||||||
|
return userDirNativePath
|
||||||
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSize(path: String): Long =
|
fun getSize(path: String): Long =
|
||||||
@ -676,19 +697,6 @@ object NativeLibrary {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
|
||||||
@JvmStatic
|
|
||||||
fun renameFile(path: String, destinationFilename: String): Boolean =
|
|
||||||
if (FileUtil.isNativePath(path)) {
|
|
||||||
try {
|
|
||||||
CitraApplication.documentsTree.renameFile(path, destinationFilename)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
FileUtil.renameFile(path, destinationFilename)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteDocument(path: String): Boolean =
|
fun deleteDocument(path: String): Boolean =
|
||||||
|
|||||||
@ -190,19 +190,6 @@ class DocumentsTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun renameFile(filepath: String, destinationFilename: String?): Boolean {
|
|
||||||
val node = resolvePath(filepath) ?: return false
|
|
||||||
try {
|
|
||||||
val filename = URLDecoder.decode(destinationFilename, FileUtil.DECODE_METHOD)
|
|
||||||
val newUri = DocumentsContract.renameDocument(context.contentResolver, node.uri!!, filename)
|
|
||||||
node.rename(filename, newUri)
|
|
||||||
return true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
error("[DocumentsTree]: Cannot rename file, error: " + e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun deleteDocument(filepath: String): Boolean {
|
fun deleteDocument(filepath: String): Boolean {
|
||||||
val node = resolvePath(filepath) ?: return false
|
val node = resolvePath(filepath) ?: return false
|
||||||
|
|||||||
@ -422,18 +422,6 @@ object FileUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun renameFile(path: String, destinationFilename: String): Boolean {
|
|
||||||
try {
|
|
||||||
val uri = Uri.parse(path)
|
|
||||||
DocumentsContract.renameDocument(context.contentResolver, uri, destinationFilename)
|
|
||||||
return true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.error("[FileUtil]: Cannot rename file, error: " + e.message)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteDocument(path: String): Boolean {
|
fun deleteDocument(path: String): Boolean {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -150,6 +150,14 @@ std::vector<std::string> GetFilesName(const std::string& filepath) {
|
|||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetUserDirectory() {
|
||||||
|
if (get_user_directory == nullptr)
|
||||||
|
throw std::runtime_error("Unable to locate user directory: Function with ID 'get_user_directory' is missing");
|
||||||
|
auto env = GetEnvForThread();
|
||||||
|
auto j_user_directory = (jstring)(env->CallStaticObjectMethod(native_library, get_user_directory));
|
||||||
|
return env->GetStringUTFChars(j_user_directory, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool CopyFile(const std::string& source, const std::string& destination_path,
|
bool CopyFile(const std::string& source, const std::string& destination_path,
|
||||||
const std::string& destination_filename) {
|
const std::string& destination_filename) {
|
||||||
if (copy_file == nullptr)
|
if (copy_file == nullptr)
|
||||||
@ -162,16 +170,6 @@ bool CopyFile(const std::string& source, const std::string& destination_path,
|
|||||||
j_destination_path, j_destination_filename);
|
j_destination_path, j_destination_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenameFile(const std::string& source, const std::string& filename) {
|
|
||||||
if (rename_file == nullptr)
|
|
||||||
return false;
|
|
||||||
auto env = GetEnvForThread();
|
|
||||||
jstring j_source_path = env->NewStringUTF(source.c_str());
|
|
||||||
jstring j_destination_path = env->NewStringUTF(filename.c_str());
|
|
||||||
return env->CallStaticBooleanMethod(native_library, rename_file, j_source_path,
|
|
||||||
j_destination_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||||
F(FunctionName, ReturnValue, JMethodID, Caller)
|
F(FunctionName, ReturnValue, JMethodID, Caller)
|
||||||
#define F(FunctionName, ReturnValue, JMethodID, Caller) \
|
#define F(FunctionName, ReturnValue, JMethodID, Caller) \
|
||||||
|
|||||||
@ -19,12 +19,12 @@
|
|||||||
open_content_uri, "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") \
|
open_content_uri, "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") \
|
||||||
V(GetFilesName, std::vector<std::string>, (const std::string& filepath), get_files_name, \
|
V(GetFilesName, std::vector<std::string>, (const std::string& filepath), get_files_name, \
|
||||||
"getFilesName", "(Ljava/lang/String;)[Ljava/lang/String;") \
|
"getFilesName", "(Ljava/lang/String;)[Ljava/lang/String;") \
|
||||||
|
V(GetUserDirectory, std::string, (), get_user_directory, "getUserDirectory", \
|
||||||
|
"()Ljava/lang/String;") \
|
||||||
V(CopyFile, bool, \
|
V(CopyFile, bool, \
|
||||||
(const std::string& source, const std::string& destination_path, \
|
(const std::string& source, const std::string& destination_path, \
|
||||||
const std::string& destination_filename), \
|
const std::string& destination_filename), \
|
||||||
copy_file, "copyFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z") \
|
copy_file, "copyFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z")
|
||||||
V(RenameFile, bool, (const std::string& source, const std::string& filename), rename_file, \
|
|
||||||
"renameFile", "(Ljava/lang/String;Ljava/lang/String;)Z")
|
|
||||||
#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
|
#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
|
||||||
V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
|
V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
|
||||||
"(Ljava/lang/String;)Z") \
|
"(Ljava/lang/String;)Z") \
|
||||||
|
|||||||
@ -311,7 +311,9 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
|
|||||||
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
|
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
|
||||||
return true;
|
return true;
|
||||||
#elif ANDROID
|
#elif ANDROID
|
||||||
if (AndroidStorage::RenameFile(srcFilename, std::string(GetFilename(destFilename))))
|
const std::string userDirLocation = AndroidStorage::GetUserDirectory();
|
||||||
|
if (rename((userDirLocation + srcFilename).c_str(),
|
||||||
|
(userDirLocation + destFilename).c_str()) == 0)
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user