Android: Add MemoryCardImage class
This commit is contained in:
parent
4dec0dee2f
commit
6d4a3bb5a5
|
@ -14,6 +14,7 @@
|
|||
#include "core/controller.h"
|
||||
#include "core/gpu.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/memory_card_image.h"
|
||||
#include "core/system.h"
|
||||
#include "frontend-common/cheevos.h"
|
||||
#include "frontend-common/game_list.h"
|
||||
|
@ -60,6 +61,8 @@ static jclass s_SaveStateInfo_class;
|
|||
static jmethodID s_SaveStateInfo_constructor;
|
||||
static jclass s_Achievement_class;
|
||||
static jmethodID s_Achievement_constructor;
|
||||
static jclass s_MemoryCardFileInfo_class;
|
||||
static jmethodID s_MemoryCardFileInfo_constructor;
|
||||
|
||||
namespace AndroidHelpers {
|
||||
JavaVM* GetJavaVM()
|
||||
|
@ -137,6 +140,58 @@ std::unique_ptr<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, j
|
|||
env->DeleteLocalRef(cls);
|
||||
return bs;
|
||||
}
|
||||
|
||||
std::vector<u8> ByteArrayToVector(JNIEnv* env, jbyteArray obj)
|
||||
{
|
||||
std::vector<u8> ret;
|
||||
const jsize size = obj ? env->GetArrayLength(obj) : 0;
|
||||
if (size > 0)
|
||||
{
|
||||
jbyte* data = env->GetByteArrayElements(obj, nullptr);
|
||||
ret.resize(static_cast<size_t>(size));
|
||||
std::memcpy(ret.data(), data, ret.size());
|
||||
env->ReleaseByteArrayElements(obj, data, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
jbyteArray NewByteArray(JNIEnv* env, const void* data, size_t size)
|
||||
{
|
||||
if (!data || size == 0)
|
||||
return nullptr;
|
||||
|
||||
jbyteArray obj = env->NewByteArray(static_cast<jsize>(size));
|
||||
jbyte* obj_data = env->GetByteArrayElements(obj, nullptr);
|
||||
std::memcpy(obj_data, data, static_cast<size_t>(static_cast<jsize>(size)));
|
||||
env->ReleaseByteArrayElements(obj, obj_data, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
jbyteArray VectorToByteArray(JNIEnv* env, const std::vector<u8>& data)
|
||||
{
|
||||
if (data.empty())
|
||||
return nullptr;
|
||||
|
||||
return NewByteArray(env, data.data(), data.size());
|
||||
}
|
||||
|
||||
jobjectArray CreateObjectArray(JNIEnv* env, jclass object_class, const jobject* objects, size_t num_objects,
|
||||
bool release_refs/* = false*/)
|
||||
{
|
||||
if (!objects || num_objects == 0)
|
||||
return nullptr;
|
||||
|
||||
jobjectArray arr = env->NewObjectArray(static_cast<jsize>(num_objects), object_class, nullptr);
|
||||
for (jsize i = 0; i < static_cast<jsize>(num_objects); i++)
|
||||
{
|
||||
env->SetObjectArrayElement(arr, i, objects[i]);
|
||||
if (release_refs && objects[i])
|
||||
env->DeleteLocalRef(objects[i]);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
} // namespace AndroidHelpers
|
||||
|
||||
AndroidHostInterface::AndroidHostInterface(jobject java_object, jobject context_object, std::string user_directory)
|
||||
|
@ -897,7 +952,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|||
// Create global reference so it doesn't get cleaned up.
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jclass string_class, host_interface_class, patch_code_class, game_list_entry_class, save_state_info_class,
|
||||
achievement_class;
|
||||
achievement_class, memory_card_file_info_class;
|
||||
if ((string_class = env->FindClass("java/lang/String")) == nullptr ||
|
||||
(s_String_class = static_cast<jclass>(env->NewGlobalRef(string_class))) == nullptr ||
|
||||
(host_interface_class = env->FindClass("com/github/stenzek/duckstation/AndroidHostInterface")) == nullptr ||
|
||||
|
@ -909,7 +964,9 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|||
(save_state_info_class = env->FindClass("com/github/stenzek/duckstation/SaveStateInfo")) == nullptr ||
|
||||
(s_SaveStateInfo_class = static_cast<jclass>(env->NewGlobalRef(save_state_info_class))) == nullptr ||
|
||||
(achievement_class = env->FindClass("com/github/stenzek/duckstation/Achievement")) == nullptr ||
|
||||
(s_Achievement_class = static_cast<jclass>(env->NewGlobalRef(achievement_class))) == nullptr)
|
||||
(s_Achievement_class = static_cast<jclass>(env->NewGlobalRef(achievement_class))) == nullptr ||
|
||||
(memory_card_file_info_class = env->FindClass("com/github/stenzek/duckstation/MemoryCardFileInfo")) == nullptr ||
|
||||
(s_MemoryCardFileInfo_class = static_cast<jclass>(env->NewGlobalRef(memory_card_file_info_class))) == nullptr)
|
||||
{
|
||||
Log_ErrorPrint("AndroidHostInterface class lookup failed");
|
||||
return -1;
|
||||
|
@ -920,6 +977,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|||
env->DeleteLocalRef(patch_code_class);
|
||||
env->DeleteLocalRef(game_list_entry_class);
|
||||
env->DeleteLocalRef(achievement_class);
|
||||
env->DeleteLocalRef(memory_card_file_info_class);
|
||||
|
||||
jclass emulation_activity_class;
|
||||
if ((s_AndroidHostInterface_constructor =
|
||||
|
@ -963,9 +1021,11 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|||
s_SaveStateInfo_class, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZII[B)V")) ==
|
||||
nullptr ||
|
||||
(s_Achievement_constructor =
|
||||
env->GetMethodID(s_Achievement_class, "<init>",
|
||||
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZ)V")) == nullptr)
|
||||
(s_Achievement_constructor = env->GetMethodID(
|
||||
s_Achievement_class, "<init>",
|
||||
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZ)V")) == nullptr ||
|
||||
(s_MemoryCardFileInfo_constructor = env->GetMethodID(s_MemoryCardFileInfo_class, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;III[[B)V")) == nullptr)
|
||||
{
|
||||
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
||||
return -1;
|
||||
|
@ -1878,3 +1938,201 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_cheevosLogout, jobject obj)
|
|||
{
|
||||
return Cheevos::Logout();
|
||||
}
|
||||
|
||||
static_assert(sizeof(MemoryCardImage::DataArray) == MemoryCardImage::DATA_SIZE);
|
||||
|
||||
static MemoryCardImage::DataArray* GetMemoryCardData(JNIEnv* env, jbyteArray obj)
|
||||
{
|
||||
if (!obj || env->GetArrayLength(obj) != MemoryCardImage::DATA_SIZE)
|
||||
return nullptr;
|
||||
|
||||
return reinterpret_cast<MemoryCardImage::DataArray*>(env->GetByteArrayElements(obj, nullptr));
|
||||
}
|
||||
|
||||
static void ReleaseMemoryCardData(JNIEnv* env, jbyteArray obj, MemoryCardImage::DataArray* data)
|
||||
{
|
||||
env->ReleaseByteArrayElements(obj, reinterpret_cast<jbyte*>(data), 0);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, MemoryCardImage_isValid, jclass clazz, jbyteArray obj)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const bool res = MemoryCardImage::IsValid(*data);
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return res;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, MemoryCardImage_format, jclass clazz, jbyteArray obj)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
MemoryCardImage::Format(data);
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jint, MemoryCardImage_getFreeBlocks, jclass clazz, jbyteArray obj)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
const u32 free_blocks = MemoryCardImage::GetFreeBlockCount(*data);
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return free_blocks;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jobjectArray, MemoryCardImage_getFiles, jclass clazz, jbyteArray obj)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
const std::vector<MemoryCardImage::FileInfo> files(MemoryCardImage::EnumerateFiles(*data));
|
||||
|
||||
std::vector<jobject> file_objects;
|
||||
file_objects.reserve(files.size());
|
||||
|
||||
jclass byteArrayClass = env->FindClass("[B");
|
||||
|
||||
for (const MemoryCardImage::FileInfo& file : files)
|
||||
{
|
||||
jobject filename = env->NewStringUTF(file.filename.c_str());
|
||||
jobject title = env->NewStringUTF(file.title.c_str());
|
||||
jobjectArray frames = nullptr;
|
||||
if (!file.icon_frames.empty())
|
||||
{
|
||||
frames = env->NewObjectArray(static_cast<jint>(file.icon_frames.size()), byteArrayClass, nullptr);
|
||||
for (jsize i = 0; i < static_cast<jsize>(file.icon_frames.size()); i++)
|
||||
{
|
||||
static constexpr jsize frame_size = MemoryCardImage::ICON_WIDTH * MemoryCardImage::ICON_HEIGHT * sizeof(u32);
|
||||
jbyteArray frame_data = env->NewByteArray(frame_size);
|
||||
jbyte* frame_data_ptr = env->GetByteArrayElements(frame_data, nullptr);
|
||||
std::memcpy(frame_data_ptr, file.icon_frames[i].pixels, frame_size);
|
||||
env->ReleaseByteArrayElements(frame_data, frame_data_ptr, 0);
|
||||
env->SetObjectArrayElement(frames, i, frame_data);
|
||||
env->DeleteLocalRef(frame_data);
|
||||
}
|
||||
}
|
||||
|
||||
file_objects.push_back(env->NewObject(s_MemoryCardFileInfo_class, s_MemoryCardFileInfo_constructor, filename, title,
|
||||
static_cast<jint>(file.size), static_cast<jint>(file.first_block),
|
||||
static_cast<int>(file.num_blocks), frames));
|
||||
|
||||
env->DeleteLocalRef(frames);
|
||||
env->DeleteLocalRef(title);
|
||||
env->DeleteLocalRef(filename);
|
||||
}
|
||||
|
||||
jobjectArray file_object_array =
|
||||
AndroidHelpers::CreateObjectArray(env, s_MemoryCardFileInfo_class, file_objects.data(), file_objects.size(), true);
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return file_object_array;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, MemoryCardImage_hasFile, jclass clazz, jbyteArray obj, jstring filename)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const std::string filename_str(AndroidHelpers::JStringToString(env, filename));
|
||||
bool result = false;
|
||||
if (!filename_str.empty())
|
||||
{
|
||||
const std::vector<MemoryCardImage::FileInfo> files(MemoryCardImage::EnumerateFiles(*data));
|
||||
result = std::any_of(files.begin(), files.end(),
|
||||
[&filename_str](const MemoryCardImage::FileInfo& fi) { return fi.filename == filename_str; });
|
||||
}
|
||||
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jbyteArray, MemoryCardImage_readFile, jclass clazz, jbyteArray obj, jstring filename)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
const std::string filename_str(AndroidHelpers::JStringToString(env, filename));
|
||||
jbyteArray ret = nullptr;
|
||||
if (!filename_str.empty())
|
||||
{
|
||||
const std::vector<MemoryCardImage::FileInfo> files(MemoryCardImage::EnumerateFiles(*data));
|
||||
auto iter = std::find_if(files.begin(), files.end(), [&filename_str](const MemoryCardImage::FileInfo& fi) {
|
||||
return fi.filename == filename_str;
|
||||
});
|
||||
if (iter != files.end())
|
||||
{
|
||||
std::vector<u8> file_data;
|
||||
if (MemoryCardImage::ReadFile(*data, *iter, &file_data))
|
||||
ret = AndroidHelpers::VectorToByteArray(env, file_data);
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, MemoryCardImage_writeFile, jclass clazz, jbyteArray obj, jstring filename,
|
||||
jbyteArray file_data)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const std::string filename_str(AndroidHelpers::JStringToString(env, filename));
|
||||
const std::vector<u8> file_data_vec(AndroidHelpers::ByteArrayToVector(env, file_data));
|
||||
bool ret = false;
|
||||
if (!filename_str.empty())
|
||||
ret = MemoryCardImage::WriteFile(data, filename_str, file_data_vec);
|
||||
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, MemoryCardImage_deleteFile, jclass clazz, jbyteArray obj, jstring filename)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const std::string filename_str(AndroidHelpers::JStringToString(env, filename));
|
||||
bool ret = false;
|
||||
|
||||
if (!filename_str.empty())
|
||||
{
|
||||
const std::vector<MemoryCardImage::FileInfo> files(MemoryCardImage::EnumerateFiles(*data));
|
||||
auto iter = std::find_if(files.begin(), files.end(), [&filename_str](const MemoryCardImage::FileInfo& fi) {
|
||||
return fi.filename == filename_str;
|
||||
});
|
||||
|
||||
if (iter != files.end())
|
||||
ret = MemoryCardImage::DeleteFile(data, *iter);
|
||||
}
|
||||
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, MemoryCardImage_importCard, jclass clazz, jbyteArray obj, jstring filename,
|
||||
jbyteArray import_data)
|
||||
{
|
||||
MemoryCardImage::DataArray* data = GetMemoryCardData(env, obj);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const std::string filename_str(AndroidHelpers::JStringToString(env, filename));
|
||||
std::vector<u8> import_data_vec(AndroidHelpers::ByteArrayToVector(env, import_data));
|
||||
bool ret = false;
|
||||
if (!filename_str.empty() && !import_data_vec.empty())
|
||||
ret = MemoryCardImage::ImportCard(data, filename_str.c_str(), std::move(import_data_vec));
|
||||
|
||||
ReleaseMemoryCardData(env, obj, data);
|
||||
return ret;
|
||||
}
|
|
@ -120,7 +120,11 @@ AndroidHostInterface* GetNativeClass(JNIEnv* env, jobject obj);
|
|||
std::string JStringToString(JNIEnv* env, jstring str);
|
||||
std::unique_ptr<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size = 65536);
|
||||
jclass GetStringClass();
|
||||
|
||||
std::vector<u8> ByteArrayToVector(JNIEnv* env, jbyteArray obj);
|
||||
jbyteArray NewByteArray(JNIEnv* env, const void* data, size_t size);
|
||||
jbyteArray VectorToByteArray(JNIEnv* env, const std::vector<u8>& data);
|
||||
jobjectArray CreateObjectArray(JNIEnv* env, jclass object_class, const jobject* objects, size_t num_objects,
|
||||
bool release_refs = false);
|
||||
} // namespace AndroidHelpers
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class MemoryCardFileInfo {
|
||||
public static final int ICON_WIDTH = 16;
|
||||
public static final int ICON_HEIGHT = 16;
|
||||
|
||||
private final String filename;
|
||||
private final String title;
|
||||
private final int size;
|
||||
private final int firstBlock;
|
||||
private final int numBlocks;
|
||||
private final byte[][] iconFrames;
|
||||
|
||||
public MemoryCardFileInfo(String filename, String title, int size, int firstBlock, int numBlocks, byte[][] iconFrames) {
|
||||
this.filename = filename;
|
||||
this.title = title;
|
||||
this.size = size;
|
||||
this.firstBlock = firstBlock;
|
||||
this.numBlocks = numBlocks;
|
||||
this.iconFrames = iconFrames;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getFirstBlock() {
|
||||
return firstBlock;
|
||||
}
|
||||
|
||||
public int getNumBlocks() {
|
||||
return numBlocks;
|
||||
}
|
||||
|
||||
public int getNumIconFrames() {
|
||||
return (iconFrames != null) ? iconFrames.length : 0;
|
||||
}
|
||||
|
||||
public byte[] getIconFrame(int index) {
|
||||
return iconFrames[index];
|
||||
}
|
||||
|
||||
public Bitmap getIconFrameBitmap(int index) {
|
||||
final byte[] data = getIconFrame(index);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
final Bitmap bitmap = Bitmap.createBitmap(ICON_WIDTH, ICON_HEIGHT, Bitmap.Config.ARGB_8888);
|
||||
bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(data));
|
||||
return bitmap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class MemoryCardImage {
|
||||
public static final int DATA_SIZE = 128 * 1024;
|
||||
public static final String FILE_EXTENSION = ".mcd";
|
||||
|
||||
private final Context context;
|
||||
private final Uri uri;
|
||||
private final byte[] data;
|
||||
|
||||
private MemoryCardImage(Context context, Uri uri, byte[] data) {
|
||||
this.context = context;
|
||||
this.uri = uri;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static String getTitleForUri(Uri uri) {
|
||||
String name = uri.getLastPathSegment();
|
||||
if (name != null) {
|
||||
final int lastSlash = name.lastIndexOf('/');
|
||||
if (lastSlash >= 0)
|
||||
name = name.substring(lastSlash + 1);
|
||||
|
||||
if (name.endsWith(FILE_EXTENSION))
|
||||
name = name.substring(0, name.length() - FILE_EXTENSION.length());
|
||||
} else {
|
||||
name = uri.toString();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static MemoryCardImage open(Context context, Uri uri) {
|
||||
byte[] data = FileUtil.readBytesFromUri(context, uri, DATA_SIZE);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
if (!isValid(data))
|
||||
return null;
|
||||
|
||||
return new MemoryCardImage(context, uri, data);
|
||||
}
|
||||
|
||||
public static MemoryCardImage create(Context context, Uri uri) {
|
||||
byte[] data = new byte[DATA_SIZE];
|
||||
format(data);
|
||||
|
||||
MemoryCardImage card = new MemoryCardImage(context, uri, data);
|
||||
if (!card.save())
|
||||
return null;
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
public static Uri[] getCardUris(Context context) {
|
||||
final String directory = "/sdcard/duckstation/memcards";
|
||||
final ArrayList<Uri> results = new ArrayList<>();
|
||||
|
||||
if (directory.charAt(0) == '/') {
|
||||
// native path
|
||||
final File directoryFile = new File(directory);
|
||||
final File[] files = directoryFile.listFiles();
|
||||
for (File file : files) {
|
||||
if (!file.isFile())
|
||||
continue;
|
||||
|
||||
if (!file.getName().endsWith(FILE_EXTENSION))
|
||||
continue;
|
||||
|
||||
results.add(Uri.fromFile(file));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
final Uri baseUri = null;
|
||||
final String[] scanProjection = new String[]{
|
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
||||
DocumentsContract.Document.COLUMN_MIME_TYPE};
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String treeDocId = DocumentsContract.getTreeDocumentId(baseUri);
|
||||
final Uri queryUri = DocumentsContract.buildChildDocumentsUriUsingTree(baseUri, treeDocId);
|
||||
final Cursor cursor = resolver.query(queryUri, scanProjection, null, null, null);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
try {
|
||||
final String mimeType = cursor.getString(2);
|
||||
final String documentId = cursor.getString(0);
|
||||
final Uri uri = DocumentsContract.buildDocumentUriUsingTree(baseUri, documentId);
|
||||
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String uriString = uri.toString();
|
||||
if (!uriString.endsWith(FILE_EXTENSION))
|
||||
continue;
|
||||
|
||||
results.add(uri);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty())
|
||||
return null;
|
||||
|
||||
Collections.sort(results, (a, b) -> a.compareTo(b));
|
||||
|
||||
final Uri[] resultArray = new Uri[results.size()];
|
||||
results.toArray(resultArray);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
private static native boolean isValid(byte[] data);
|
||||
|
||||
private static native void format(byte[] data);
|
||||
|
||||
private static native int getFreeBlocks(byte[] data);
|
||||
|
||||
private static native MemoryCardFileInfo[] getFiles(byte[] data);
|
||||
|
||||
private static native boolean hasFile(byte[] data, String filename);
|
||||
|
||||
private static native byte[] readFile(byte[] data, String filename);
|
||||
|
||||
private static native boolean writeFile(byte[] data, String filename, byte[] fileData);
|
||||
|
||||
private static native boolean deleteFile(byte[] data, String filename);
|
||||
|
||||
private static native boolean importCard(byte[] data, String filename, byte[] importData);
|
||||
|
||||
public boolean save() {
|
||||
return FileUtil.writeBytesToUri(context, uri, data);
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return FileUtil.deleteFileAtUri(context, uri);
|
||||
}
|
||||
|
||||
public boolean format() {
|
||||
format(data);
|
||||
return save();
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return getTitleForUri(uri);
|
||||
}
|
||||
|
||||
public int getFreeBlocks() {
|
||||
return getFreeBlocks(data);
|
||||
}
|
||||
|
||||
public MemoryCardFileInfo[] getFiles() {
|
||||
return getFiles(data);
|
||||
}
|
||||
|
||||
public boolean hasFile(String filename) {
|
||||
return hasFile(data, filename);
|
||||
}
|
||||
|
||||
public byte[] readFile(String filename) {
|
||||
return readFile(data, filename);
|
||||
}
|
||||
|
||||
public boolean writeFile(String filename, byte[] fileData) {
|
||||
if (!writeFile(data, filename, fileData))
|
||||
return false;
|
||||
|
||||
return save();
|
||||
}
|
||||
|
||||
public boolean deleteFile(String filename) {
|
||||
if (!deleteFile(data, filename))
|
||||
return false;
|
||||
|
||||
return save();
|
||||
}
|
||||
|
||||
public boolean importCard(String filename, byte[] importData) {
|
||||
if (!importCard(data, filename, importData))
|
||||
return false;
|
||||
|
||||
return save();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue